lwgeom/0000755000176200001440000000000014432644332011551 5ustar liggesuserslwgeom/NAMESPACE0000644000176200001440000000310014100245326012752 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(st_as_sfc,TWKB) S3method(st_force_polygon_cw,sf) S3method(st_force_polygon_cw,sfc) S3method(st_force_polygon_cw,sfg) S3method(st_is_polygon_cw,sf) S3method(st_is_polygon_cw,sfc) S3method(st_is_polygon_cw,sfg) S3method(st_linesubstring,sf) S3method(st_linesubstring,sfc) S3method(st_linesubstring,sfg) S3method(st_minimum_bounding_circle,sf) S3method(st_minimum_bounding_circle,sfc) S3method(st_minimum_bounding_circle,sfg) S3method(st_snap_to_grid,sf) S3method(st_snap_to_grid,sfc) S3method(st_snap_to_grid,sfg) S3method(st_split,sf) S3method(st_split,sfc) S3method(st_split,sfg) S3method(st_subdivide,sf) S3method(st_subdivide,sfc) S3method(st_subdivide,sfg) S3method(st_transform_proj,sf) S3method(st_transform_proj,sfc) S3method(st_transform_proj,sfg) S3method(st_wrap_x,sf) S3method(st_wrap_x,sfc) S3method(st_wrap_x,sfg) export(lwgeom_extSoftVersion) export(lwgeom_make_valid) export(st_asewkt) export(st_astext) export(st_endpoint) export(st_force_polygon_cw) export(st_geod_area) export(st_geod_azimuth) export(st_geod_covered_by) export(st_geod_covers) export(st_geod_distance) export(st_geod_length) export(st_geod_segmentize) export(st_geohash) export(st_is_polygon_cw) export(st_linesubstring) export(st_minimum_bounding_circle) export(st_perimeter) export(st_perimeter_2d) export(st_snap_to_grid) export(st_split) export(st_startpoint) export(st_subdivide) export(st_transform_proj) export(st_wrap_x) import(sf) importFrom(Rcpp,evalCpp) importFrom(units,as_units) importFrom(units,set_units) importFrom(utils,tail) useDynLib(lwgeom) lwgeom/tools/0000755000176200001440000000000014027646452012716 5ustar liggesuserslwgeom/tools/winlibs.R0000644000176200001440000000125013773172540014505 0ustar liggesusersif(getRversion() < "3.3.0") { stop("Your version of R is too old. This package requires R-3.3.0 or newer on Windows.") } # For details see: https://github.com/rwinlib/gdal3 VERSION <- commandArgs(TRUE) # wrong path: if(!file.exists(sprintf("../windows/gdal3-%s/include/gdal/gdal.h", VERSION))){ testfile <- sprintf("../windows/gdal3-%s/include/gdal-%s/gdal.h", VERSION, VERSION) if(!file.exists(testfile)){ if(getRversion() < "3.3.0") setInternet2() download.file(sprintf("https://github.com/rwinlib/gdal3/archive/v%s.zip", VERSION), "lib.zip", quiet = TRUE) dir.create("../windows", showWarnings = FALSE) unzip("lib.zip", exdir = "../windows") unlink("lib.zip") } lwgeom/man/0000755000176200001440000000000014076540103012317 5ustar liggesuserslwgeom/man/st_transform_proj.Rd0000644000176200001440000000342113773172540016372 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/transform.R \name{st_transform_proj} \alias{st_transform_proj} \alias{st_transform_proj.sfc} \alias{st_transform_proj.sf} \alias{st_transform_proj.sfg} \title{Transform or convert coordinates of simple features directly with Proj.4 (bypassing GDAL)} \usage{ st_transform_proj(x, crs, ...) \method{st_transform_proj}{sfc}(x, crs, ...) \method{st_transform_proj}{sf}(x, crs, ...) \method{st_transform_proj}{sfg}(x, crs, ...) } \arguments{ \item{x}{object of class sf, sfc or sfg} \item{crs}{character; target CRS, or, in case of a length 2 character vector, source and target CRS} \item{...}{ignored} } \description{ Transform or convert coordinates of simple features directly with Proj.4 (bypassing GDAL) } \details{ Transforms coordinates of object to new projection, using PROJ directly rather than the GDAL API used by \link[sf]{st_transform}. If \code{crs} is a single CRS, it forms the target CRS, and in that case the source CRS is obtained as \code{st_crs(x)}. Since this presumes that the source CRS is accepted by GDAL (which is not always the case), a second option is to specify the source and target CRS as two proj4strings in argument \code{crs}. In the latter case, \code{st_crs(x)} is ignored and may well be \code{NA}. The \code{st_transform_proj} method for \code{sfg} objects assumes that the CRS of the object is available as an attribute of that name. } \examples{ library(sf) p1 = st_point(c(7,52)) p2 = st_point(c(-30,20)) sfc = st_sfc(p1, p2, crs = 4326) sfc st_transform_proj(sfc, "+proj=wintri") library(sf) nc = st_read(system.file("shape/nc.shp", package="sf")) st_transform_proj(nc[1,], "+proj=wintri +over") st_transform_proj(structure(p1, proj4string = "+init=epsg:4326"), "+init=epsg:3857") } lwgeom/man/st_geohash.Rd0000644000176200001440000000161213773172540014743 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geohash.R \name{st_geohash} \alias{st_geohash} \title{compute geohash from (average) coordinates} \usage{ st_geohash(x, precision = 0) } \arguments{ \item{x}{object of class \code{sf}, \code{sfc} or \code{sfg}} \item{precision}{integer; precision (length) of geohash returned. From the liblwgeom source: ``where the precision is non-positive, a precision based on the bounds of the feature. Big features have loose precision. Small features have tight precision.''} } \value{ character vector with geohashes } \description{ compute geohash from (average) coordinates } \details{ see \url{http://geohash.org/} or \url{https://en.wikipedia.org/wiki/Geohash}. } \examples{ library(sf) lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 2) lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 10) } lwgeom/man/st_split.Rd0000644000176200001440000000130513773172540014457 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/split.R \name{st_split} \alias{st_split} \title{Return a collection of geometries resulting by splitting a geometry} \usage{ st_split(x, y) } \arguments{ \item{x}{object with geometries to be splitted} \item{y}{object split with (blade); if \code{y} contains more than one feature geometry, the geometries are \link[sf:geos_combine]{st_combine} 'd} } \value{ object of the same class as \code{x} } \description{ Return a collection of geometries resulting by splitting a geometry } \examples{ library(sf) l = st_as_sfc('MULTILINESTRING((10 10, 190 190), (15 15, 30 30, 100 90))') pt = st_sfc(st_point(c(30,30))) st_split(l, pt) } lwgeom/man/perimeter.Rd0000644000176200001440000000101613773172540014611 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/perimeter.R \name{perimeter} \alias{perimeter} \alias{st_perimeter} \alias{st_perimeter_2d} \title{compute perimeter from polygons or other geometries} \usage{ st_perimeter(x) st_perimeter_2d(x) } \arguments{ \item{x}{object of class \code{sf}, \code{sfc} or \code{sfg}} } \value{ numerical vector with perimeter for each feature (geometry), with unit of measure when possible } \description{ compute perimeter from polygons or other geometries } lwgeom/man/st_astext.Rd0000644000176200001440000000237014406665652014644 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wkt.R \name{st_astext} \alias{st_astext} \alias{st_asewkt} \title{Return Well-known Text representation of simple feature geometry} \usage{ st_astext(x, digits = options("digits"), ..., EWKT = FALSE) st_asewkt(x, digits = options("digits")) } \arguments{ \item{x}{object of class \code{sfg}, \code{sfc}, or \code{sf}} \item{digits}{integer; number of decimal digits to print} \item{...}{ignored} \item{EWKT}{logical; use PostGIS Enhanced WKT (includes srid)} } \description{ Return Well-known Text representation of simple feature geometry or coordinate reference system } \details{ The returned WKT representation of simple feature geometry conforms to the \href{https://www.ogc.org/standard/sfa/}{simple features access} specification and extensions (if \code{EWKT = TRUE}), \href{http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT}{known as EWKT}, supported by PostGIS and other simple features implementations for addition of SRID to a WKT string. \code{st_asewkt()} returns the Well-Known Text (WKT) representation of the geometry with SRID meta data. } \examples{ library(sf) pt <- st_sfc(st_point(c(1.0002,2.3030303)), crs = 4326) st_astext(pt, 3) st_asewkt(pt, 3) } lwgeom/man/bounding_circle.Rd0000644000176200001440000000227513773172540015753 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bounding_circle.R \name{bounding_circle} \alias{bounding_circle} \alias{st_minimum_bounding_circle} \title{Generate the minimum bounding circle} \usage{ st_minimum_bounding_circle(x, nQuadSegs = 30) } \arguments{ \item{x}{object of class \code{sfg}, \code{sfg} or \code{sf}} \item{nQuadSegs}{number of segments per quadrant (passed to \code{st_buffer})} } \value{ Object of the same class as \code{x} } \description{ Generate the minimum bounding circle } \details{ \code{st_minimum_bounding_circle} uses the \code{lwgeom_calculate_mbc} method also used by the PostGIS command \code{ST_MinimumBoundingCircle}. } \examples{ library(sf) x = st_multipoint(matrix(c(0,1,0,1),2,2)) y = st_multipoint(matrix(c(0,0,1,0,1,1),3,2)) mbcx = st_minimum_bounding_circle(x) mbcy = st_minimum_bounding_circle(y) if (.Platform$OS.type != "windows") { plot(mbcx, axes=TRUE); plot(x, add=TRUE) plot(mbcy, axes=TRUE); plot(y, add=TRUE) } nc = st_read(system.file("gpkg/nc.gpkg", package="sf")) state = st_union(st_geometry(nc)) if (.Platform$OS.type != "windows") { plot(st_minimum_bounding_circle(state), asp=1) plot(state, add=TRUE) } } lwgeom/man/st_as_sfc.TWKB.Rd0000644000176200001440000000113413773172540015330 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/twkb.R \name{st_as_sfc.TWKB} \alias{st_as_sfc.TWKB} \title{create sfc object from tiny well-known binary (twkb)} \usage{ \method{st_as_sfc}{TWKB}(x, ...) } \arguments{ \item{x}{list with raw vectors, of class \code{TWKB}} \item{...}{ignored} } \description{ create sfc object from tiny well-known binary (twkb) } \examples{ l = structure(list(as.raw(c(0x02, 0x00, 0x02, 0x02, 0x02, 0x08, 0x08))), class = "TWKB") library(sf) # load generic st_as_sfc(l) } \seealso{ https://github.com/TWKB/Specification/blob/master/twkb.md } lwgeom/man/st_linesubstring.Rd0000644000176200001440000000171313773172540016217 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/split.R \name{st_linesubstring} \alias{st_linesubstring} \title{get substring from linestring} \usage{ st_linesubstring(x, from, to, tolerance, ...) } \arguments{ \item{x}{object of class \code{sfc}, \code{sf} or \code{sfg}} \item{from}{relative distance from origin (in [0,1])} \item{to}{relative distance from origin (in [0,1])} \item{tolerance}{tolerance parameter, when to snap to line node node} \item{...}{ignored} } \value{ object of class \code{sfc} } \description{ get substring from linestring } \examples{ library(sf) lines = st_sfc(st_linestring(rbind(c(0,0), c(1,2), c(2,0))), crs = 4326) spl = st_linesubstring(lines, 0.2, 0.8) # should warn plot(st_geometry(lines), col = 'red', lwd = 3) plot(spl, col = 'black', lwd = 3, add = TRUE) st_linesubstring(lines, 0.49999, 0.8) # three points st_linesubstring(lines, 0.49999, 0.8, 0.001) # two points: snap start to second node } lwgeom/man/st_snap_to_grid.Rd0000644000176200001440000000164613773172540016004 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/snap_to_grid.R \name{st_snap_to_grid} \alias{st_snap_to_grid} \title{Snap geometries to a grid} \usage{ st_snap_to_grid(x, size, origin) } \arguments{ \item{x}{object with geometries to be snapped} \item{size}{numeric or (length) units object; grid cell size in x-, y- (and possibly z- and m-) directions} \item{origin}{numeric; origin of the grid} } \value{ object of the same class as \code{x} } \description{ Snap geometries to a grid } \examples{ # obtain data library(sf) x = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE)[1, ] \%>\% st_geometry \%>\% st_transform(3395) # snap to a grid of 5000 m err = try(y <- st_snap_to_grid(x, 5000)) # plot data for visual comparison if (!inherits(err, "try-error")) { opar = par(mfrow = c(1, 2)) plot(x, main = "orginal data") plot(y, main = "snapped to 5000 m") par(opar) } } lwgeom/man/lwgeom_make_valid.Rd0000644000176200001440000000046113773172540016266 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/valid.R \name{lwgeom_make_valid} \alias{lwgeom_make_valid} \title{Make an invalid geometry valid} \usage{ lwgeom_make_valid(x) } \arguments{ \item{x}{object of class \code{sfc}} } \description{ Make an invalid geometry valid } lwgeom/man/st_geod_azimuth.Rd0000644000176200001440000000070713773172540016010 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bearing.R \name{st_geod_azimuth} \alias{st_geod_azimuth} \title{compute azimuth between sequence of points} \usage{ st_geod_azimuth(x) } \arguments{ \item{x}{object of class \code{sf}, \code{sfc} or \code{sfg}} } \description{ compute azimuth between sequence of points } \examples{ library(sf) p = st_sfc(st_point(c(7,52)), st_point(c(8,53)), crs = 4326) st_geod_azimuth(p) } lwgeom/man/st_startpoint.Rd0000644000176200001440000000145213773172540015536 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/startpoint.R \name{st_startpoint} \alias{st_startpoint} \alias{st_endpoint} \title{Return the start and end points from lines} \usage{ st_startpoint(x) st_endpoint(x) } \arguments{ \item{x}{line of class \code{sf}, \code{sfc} or \code{sfg}} } \value{ \code{sf} object representing start and end points } \description{ Return the start and end points from lines } \details{ see \url{https://postgis.net/docs/ST_StartPoint.html} and \url{https://postgis.net/docs/ST_EndPoint.html}. } \examples{ library(sf) m = matrix(c(0, 1, 2, 0, 1, 4), ncol = 2) l = st_sfc(st_linestring(m)) lwgeom::st_startpoint(l) lwgeom::st_endpoint(l) l2 = st_sfc(st_linestring(m), st_linestring(m[3:1, ])) lwgeom::st_startpoint(l2) lwgeom::st_endpoint(l2) } lwgeom/man/st_force_polygon_cw.Rd0000644000176200001440000000150713773172540016666 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clockwise.R \name{st_force_polygon_cw} \alias{st_force_polygon_cw} \title{Force a POLYGON or MULTIPOLYGON to be clockwise} \usage{ st_force_polygon_cw(x) } \arguments{ \item{x}{object with polygon geometries} } \value{ object of the same class as \code{x} } \description{ Check if a POLYGON or MULTIPOLYGON is clockwise, and if not make it so. According to the 'Right-hand-rule', outer rings should be clockwise, and inner holes should be counter-clockwise } \examples{ library(sf) polys <- st_sf(cw = c(FALSE, TRUE), st_as_sfc(c('POLYGON ((0 0, 1 0, 1 1, 0 0))', 'POLYGON ((1 1, 2 2, 2 1, 1 1))'))) st_force_polygon_cw(polys) st_force_polygon_cw(st_geometry(polys)) st_force_polygon_cw(st_geometry(polys)[[1]]) } lwgeom/man/st_subdivide.Rd0000644000176200001440000000115513773172540015305 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/subdivide.R \name{st_subdivide} \alias{st_subdivide} \title{Return a collection of geometries resulting by subdividing a geometry} \usage{ st_subdivide(x, max_vertices) } \arguments{ \item{x}{object with geometries to be subdivided} \item{max_vertices}{integer; maximum size of the subgeometries (at least 8)} } \value{ object of the same class as \code{x} } \description{ Return a collection of geometries resulting by subdividing a geometry } \examples{ library(sf) demo(nc, ask = FALSE, echo = FALSE) x = st_subdivide(nc, 10) plot(x[1]) } lwgeom/man/lwgeom_extSoftVersion.Rd0000644000176200001440000000053413773172540017175 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/init.R \name{lwgeom_extSoftVersion} \alias{lwgeom_extSoftVersion} \title{Provide the external dependencies versions of the libraries linked to sf} \usage{ lwgeom_extSoftVersion() } \description{ Provide the external dependencies versions of the libraries linked to sf } lwgeom/man/geod.Rd0000644000176200001440000000421513773172540013537 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geod.R \name{geod} \alias{geod} \alias{st_geod_area} \alias{st_geod_length} \alias{st_geod_segmentize} \alias{st_geod_covers} \alias{st_geod_covered_by} \alias{st_geod_distance} \title{liblwgeom geodetic functions} \usage{ st_geod_area(x) st_geod_length(x) st_geod_segmentize(x, max_seg_length) st_geod_covers(x, y, sparse = TRUE) st_geod_covered_by(x, y, sparse = TRUE) st_geod_distance(x, y, tolerance = 0, sparse = FALSE) } \arguments{ \item{x}{object of class \code{sf}, \code{sfc} or \code{sfg}} \item{max_seg_length}{segment length in degree, radians, or as a length unit (e.g., m)} \item{y}{object of class \code{sf}, \code{sfc} or \code{sfg}} \item{sparse}{logical; if \code{TRUE}, return a sparse matrix (object of class \code{sgbp}), otherwise, return a dense logical matrix.} \item{tolerance}{double or length \code{units} value: if positive, the first distance less than \code{tolerance} is returned, rather than the true distance} } \description{ liblwgeom geodetic functions for length, area, segmentizing, covers } \details{ \code{st_area} will give an error message when the area spans the equator and \code{lwgeom} is linked to a proj.4 version older than 4.9.0 (see \link{lwgeom_extSoftVersion}) longitude coordinates returned are rescaled to [-180,180) } \note{ this function should is used by \link[sf:geos_measures]{st_distance}, do not use it directly } \examples{ library(sf) nc = st_read(system.file("gpkg/nc.gpkg", package="sf")) st_geod_area(nc[1:3,]) # st_area(nc[1:3,]) l = st_sfc(st_linestring(rbind(c(7,52), c(8,53))), crs = 4326) st_geod_length(l) library(units) pol = st_polygon(list(rbind(c(0,0), c(0,60), c(60,60), c(0,0)))) x = st_sfc(pol, crs = 4326) seg = st_geod_segmentize(x[1], set_units(10, km)) plot(seg, graticule = TRUE, axes = TRUE) pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) pt = st_point(c(0,90)) x = st_sfc(pole, pt, crs = 4326) st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)]) pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) pt = st_point(c(30,70)) x = st_sfc(pole, pt, crs = 4326) st_geod_distance(x, x) } lwgeom/man/st_wrap_x.Rd0000644000176200001440000000136714076540103014623 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wrap_x.R \name{st_wrap_x} \alias{st_wrap_x} \title{Splits input geometries by a vertical line and moves components falling on one side of that line by a fixed amount} \usage{ st_wrap_x(x, wrap, move) } \arguments{ \item{x}{object with geometries to be split} \item{wrap}{x value of split line} \item{move}{amount by which geometries falling to the left of the line should be translated to the right} } \value{ object of the same class as \code{x} } \description{ Splits input geometries by a vertical line and moves components falling on one side of that line by a fixed amount } \examples{ library(sf) demo(nc, ask = FALSE, echo = FALSE) x = st_wrap_x(nc, -78, 10) plot(x[1]) } lwgeom/man/st_is_polygon_cw.Rd0000644000176200001440000000145713773172540016207 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clockwise.R \name{st_is_polygon_cw} \alias{st_is_polygon_cw} \title{Check if a POLYGON or MULTIPOLYGON is clockwise} \usage{ st_is_polygon_cw(x) } \arguments{ \item{x}{object with polygon geometries} } \value{ logical with length the same number of features in `x` } \description{ Check if a POLYGON or MULTIPOLYGON is clockwise. According to the 'Right-hand-rule', outer rings should be clockwise, and inner holes should be counter-clockwise } \examples{ library(sf) polys <- st_sf(cw = c(FALSE, TRUE), st_as_sfc(c('POLYGON ((0 0, 1 0, 1 1, 0 0))', 'POLYGON ((1 1, 2 2, 2 1, 1 1))'))) st_is_polygon_cw(polys) st_is_polygon_cw(st_geometry(polys)) st_is_polygon_cw(st_geometry(polys)[[1]]) } lwgeom/DESCRIPTION0000644000176200001440000000270114432644332013257 0ustar liggesusersPackage: lwgeom Version: 0.2-13 Title: Bindings to Selected 'liblwgeom' Functions for Simple Features Description: Access to selected functions found in 'liblwgeom' , the light-weight geometry library used by 'PostGIS' . Authors@R: c(person("Edzer", "Pebesma", role = c("aut", "cre"), email = "edzer.pebesma@uni-muenster.de", comment = c(ORCID = "0000-0001-8049-7069")), person("Colin", "Rundel", role = "ctb"), person("Andy", "Teucher", role = "ctb"), person("liblwgeom developers", role = "cph")) Depends: R (>= 3.3.0) Imports: Rcpp, units, sf (>= 0.9-3) Suggests: covr, sp, geosphere, testthat LinkingTo: Rcpp, sf (>= 0.6-0) SystemRequirements: GEOS (>= 3.5.0), PROJ (>= 4.8.0), sqlite3 License: GPL-2 Copyright: file COPYRIGHTS URL: https://github.com/r-spatial/lwgeom/ BugReports: https://github.com/r-spatial/lwgeom/issues/ Collate: init.R RcppExports.R geohash.R split.R subdivide.R valid.R transform.R bounding_circle.R bearing.R snap_to_grid.R startpoint.R twkb.R perimeter.R clockwise.R geod.R wkt.R wrap_x.R RoxygenNote: 7.2.3 NeedsCompilation: yes Packaged: 2023-05-22 08:43:01 UTC; edzer Author: Edzer Pebesma [aut, cre] (), Colin Rundel [ctb], Andy Teucher [ctb], liblwgeom developers [cph] Maintainer: Edzer Pebesma Repository: CRAN Date/Publication: 2023-05-22 10:50:02 UTC lwgeom/tests/0000755000176200001440000000000014100240677012707 5ustar liggesuserslwgeom/tests/testthat/0000755000176200001440000000000014432644332014553 5ustar liggesuserslwgeom/tests/testthat/test_lwgeom.R0000644000176200001440000001210114315324152017215 0ustar liggesuserscontext("lwgeom") test_that("st_make_valid works", { library(sf) x = st_sfc(st_polygon(list(rbind(c(0,0),c(0.5,0),c(0.5,0.5),c(0.5,0),c(1,0),c(1,1),c(0,1),c(0,0))))) fls = suppressWarnings(sf::st_is_valid(x, FALSE)) expect_false(fls) y = st_make_valid(x) expect_true(st_is_valid(y)) expect_true(st_is_valid(st_make_valid(x[[1]]))) expect_true(st_is_valid(lwgeom_make_valid(x))) expect_true(st_is_valid(st_make_valid(st_sf(a = 1, geom = x)))) expect_equal(lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 2), c( "s0","up")) expect_equal(lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 10), c("s095fjhkbx","upbpbpbpbp")) l = st_as_sfc('MULTILINESTRING((10 10, 190 190), (15 15, 30 30, 100 90))') pt = st_sfc(st_point(c(30,30))) expect_silent(lwgeom::st_split(l, pt)) # sfc expect_silent(lwgeom::st_split(l[[1]], pt)) # sfg expect_silent(lwgeom::st_split(st_sf(a = 1, geom = l), pt)) # sf # https://github.com/r-spatial/sf/issues/509 : p1 = st_point(c(7,52)) geom.sf = st_sfc(p1, crs = 4326) x <- st_transform_proj(geom.sf, "+proj=wintri") if (sf_extSoftVersion()["proj.4"] >= "6.0.0") { x2 <- st_transform_proj(geom.sf, c("EPSG:4326", "+proj=wintri")) } x3 <- st_transform_proj(geom.sf, st_crs(3857)) p = st_crs(4326)$proj4string x <- st_transform_proj(structure(geom.sf[[1]], proj4string = p), "+proj=wintri") nc = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) st_transform_proj(nc[1,], "+proj=wintri +over") lwgeom_extSoftVersion() }) #test_that("st_minimum_bounding_circle works", { # library(sf) # x = st_multipoint(matrix(c(0,1,0,1),2,2)) # y = st_multipoint(matrix(c(0,0,1,0,1,1),3,2)) # plot(st_minimum_bounding_circle(x), axes=TRUE); plot(x, add=TRUE) # plot(st_minimum_bounding_circle(y), axes=TRUE); plot(y, add=TRUE) # nc = st_read(system.file("shape/nc.shp", package="sf"), quiet = TRUE) # state = st_union(st_geometry(nc)) # st_minimum_bounding_circle(state) # st_minimum_bounding_circle(st_sf(st = "nc", geom = state)) #}) test_that("st_subdivide works", { library(sf) x = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) expect_silent(st_subdivide(x, 10)) expect_silent(st_subdivide(st_geometry(x), 10)) expect_silent(st_subdivide(st_geometry(x)[[1]], 10)) }) test_that("st_snap_to_grid_works", { # make data library(sf) x = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) %>% st_transform(3395) # snap to grid err <- try(y1 <- st_snap_to_grid(x, 5000), silent = TRUE) if (inherits(err, "try-error")) # not available in liblwgeom < 2.5.0 skip("snap_to_grid not available in this liblwgeom version") y2 = st_snap_to_grid(st_geometry(x), 5000) y3 = st_snap_to_grid(st_geometry(x)[[1]], 5000) # check that output class match inputs expect_is(y1, "sf") expect_is(y2, "sfc") expect_is(y3, "sfg") # check that outputs contain correct number of geometries expect_equal(nrow(x), nrow(y1)) expect_equal(nrow(x), length(y2)) # check that outputs have correct crs expect_equal(st_crs(x), st_crs(y1)) expect_equal(st_crs(x), st_crs(y2)) # check that outputs have been snapped correctly, # i.e. the coordinates of the geometries divided by the tolerance (5000) # should not yield a remainder y1_m <- do.call(rbind, lapply(st_cast(st_geometry(y1), "MULTIPOINT"), as.matrix)) expect_true(all(c(y1_m %% 5000) == 0)) y2_m <- do.call(rbind, lapply(st_cast(y2, "MULTIPOINT"), as.matrix)) expect_true(all(c(y2_m %% 5000) == 0)) expect_true(all(c(as.matrix(st_cast(y3, "MULTIPOINT")) %% 5000) == 0)) }) #test_that("st_transform_proj finds sf's PROJ files", { # # skip_on_os("mac") # FIXME: in sf rather than here # library(sf) # nc <- st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) # bb1 = st_bbox(nc) # bb2 = st_bbox(st_transform(nc, "+proj=longlat")) # bb3 = st_bbox(st_transform_proj(nc, "+proj=longlat")) # bb4 = st_bbox(st_transform_proj(nc, st_crs(4326)$proj4string)) # # expect_false(any(bb1 == bb2)) # # expect_true(all.equal(as.numeric(bb2), as.numeric(bb3))) # # expect_true(all.equal(as.numeric(bb4), as.numeric(bb3))) #}) test_that("st_linesubstring warns on 4326", { library(sf) lines = st_sfc(st_linestring(rbind(c(0,0), c(1,2), c(2,0))), crs = 4326) library(lwgeom) expect_warning(spl <- st_linesubstring(lines, 0.2, 0.8)) expect_silent(spl <- st_linesubstring(lines[[1]], 0.2, 0.8)) # sfg has no crs expect_warning(spl <- st_linesubstring(st_sf(lines), 0.2, 0.8)) plot(st_geometry(lines), col = 'red', lwd = 3) plot(spl, col = 'black', lwd = 3, add = TRUE) }) #test_that("st_startpoint works", { # library(sf) # library(lwgeom) # sp = st_startpoint(st_sfc(st_linestring(matrix(1:10,,2)), st_linestring(matrix(3:12,,2)),crs=4326)) #}) test_that("st_wrap_x works", { library(sf) library(lwgeom) nc <- st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) splitline <- -78 offset <- 10 x <- st_wrap_x(nc, splitline, offset) expect_equal(nrow(x), nrow(nc)) expect_equal(st_bbox(x)$xmin, splitline, check.attributes = FALSE) expect_equal(st_bbox(x)$xmax, splitline + offset, check.attributes = FALSE) }) lwgeom/tests/testthat/test-clockwise.R0000644000176200001440000000722613773172540017651 0ustar liggesuserscontext("test-clockwise.R") library(sf) polys <- st_sf(cw = c(FALSE, TRUE), st_as_sfc(c('POLYGON ((0 0, 1 0, 1 1, 0 0))', 'POLYGON ((1 1, 2 2, 2 1, 1 1))'))) test_that("st_is_polygon_cw works on all classes", { expect_equal(c(FALSE, TRUE), st_is_polygon_cw(polys)) expect_equal(c(FALSE, TRUE), st_is_polygon_cw(st_geometry(polys))) expect_false(st_is_polygon_cw(st_geometry(polys)[[1]])) }) test_that("st_force_polygon_cw works on all classes", { out_sf <- st_force_polygon_cw(polys) out_sfc <- st_force_polygon_cw(st_geometry(polys)) out_sfg <- st_force_polygon_cw(st_geometry(polys)[[1]]) expect_true(all(st_is_polygon_cw(out_sf))) expect_true(all(st_is_polygon_cw(out_sfc))) expect_true(all(st_is_polygon_cw(out_sfg))) }) test_that("st_force_polygon_cw preserves crs", { st_crs(polys) <- 4326 expect_equal(st_crs(st_force_polygon_cw(polys)), st_crs(polys)) expect_equal(st_crs(st_force_polygon_cw(st_geometry(polys))), st_crs(polys)) }) test_that("st_force_polyfon_cw works with Single polygon, ccw exterior ring only", { obj <- st_as_sfc('POLYGON ((0 0, 1 0, 1 1, 0 0))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with Single polygon, cw exterior ring only", { obj <- st_as_sfc('POLYGON ((0 0, 1 1, 1 0, 0 0))') expect_true(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with Single polygon, ccw exterior ring, cw interior rings", { obj <- st_as_sfc('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 5 7, 7 7, 5 5))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with Single polygon, cw exterior ring, ccw interior rings", { obj <- st_as_sfc('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 7 7, 5 7, 5 5))') expect_true(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with Single polygon, ccw exterior ring, mixed interior rings", { obj <- st_as_sfc('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 7 7, 5 7, 5 5))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with Single polygon, cw exterior ring, mixed interior rings", { obj <- st_as_sfc('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 5 7, 7 7, 5 5))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with MultiPolygon, ccw exterior rings only", { obj <- st_as_sfc('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 0, 101 1, 100 0)))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with MultiPolygon, cw exterior rings only", { obj <- st_as_sfc('MULTIPOLYGON (((0 0, 1 1, 1 0, 0 0)), ((100 0, 101 1, 101 0, 100 0)))') expect_true(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) test_that("st_force_polyfon_cw works with MultiPolygon, mixed exterior rings", { obj <- st_as_sfc('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 1, 101 0, 100 0)))') expect_false(st_is_polygon_cw(obj)) ret <- st_force_polygon_cw(obj) expect_true(all(st_is_polygon_cw(ret))) }) lwgeom/tests/testthat/test-as_text.R0000644000176200001440000000141713773172540017331 0ustar liggesuserscontext("test-as_text.R") library(sf) test_that("Prints Points", { pt <- st_sfc(st_point(c(1.0002,2.3030303)), crs = 4326) expect_equal(st_asewkt(pt, 1), "SRID=4326;POINT(1 2.3)") expect_equal(st_astext(pt, 2, EWKT = FALSE), "POINT(1 2.3)") expect_equal(st_astext(pt, 3, EWKT = FALSE), "POINT(1 2.303)") expect_equal(st_astext(pt, 10, EWKT = FALSE), "POINT(1.0002 2.3030303)") }) test_that("Prints Polygons and Lines", { pol <- st_sfc(st_polygon(list( rbind(c(0,0),c(0.5,0),c(0.5,0.5),c(0.5,0),c(1,0),c(1,1),c(0,1),c(0,0)) ))) txt <- "POLYGON((0 0,0.5 0,0.5 0.5,0.5 0,1 0,1 1,0 1,0 0))" expect_equal(st_astext(pol), txt) ln <- st_cast(pol, "LINESTRING") txt <- "LINESTRING(0 0,0.5 0,0.5 0.5,0.5 0,1 0,1 1,0 1,0 0)" expect_equal(st_astext(ln), txt) }) lwgeom/tests/geod.R0000644000176200001440000000301313773172540013755 0ustar liggesusers### Name: lw_geodetic ### Title: geodetic length, area, and predicates ### Aliases: lw_geodetic st_geod_area lw_geodetic st_geod_length ### lw_geodetic st_geod_segmentize lw_geodetic st_geod_covers ### ** Examples suppressPackageStartupMessages(library(sf)) suppressPackageStartupMessages(library(lwgeom)) suppressPackageStartupMessages(library(units)) nc = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) st_geod_area(nc[1:3,]) # st_area(nc[1:3,]) l = st_sfc(st_linestring(rbind(c(7,52), c(8,53))), crs = 4326) st_geod_length(l) pol = st_polygon(list(rbind(c(0,0), c(0,60), c(60,60), c(0,0)))) x = st_sfc(pol, crs = 4326) seg = st_geod_segmentize(x[1], set_units(10, km)) plot(seg, graticule = TRUE, axes = TRUE) pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) pt = st_point(c(0,90)) x = st_sfc(pole, pt, crs = 4326) st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)]) st_geod_covered_by(x[c(2,2)], x[c(1,1,1)]) st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)], sparse = FALSE) st_geod_covered_by(x[c(2,2)], x[c(1,1,1)], sparse = FALSE) # box crossing the dateline: #box = st_polygon(list(rbind(c(179.5,0), c(179.5,1), c(-179.5,1), c(-179.5,0), c(179.5,0)))) box = st_polygon(list(rbind(c(179.5,0.1), c(179.5,1), c(-179.5,1), c(-179.5,0.1), c(179.5,0.1)))) b = st_sfc(box, crs = 4326) units::set_units(st_geod_area(b), km^2) # approx 111^2 pt = st_point(c(30, 70)) x = st_sfc(pole, pt, pt, crs = 4326) st_geod_distance(x, x) st_geod_distance(x, x, sparse = TRUE) st_geod_distance(x, x, tolerance = 1, sparse = TRUE) lwgeom/tests/perimeter.Rout.save0000644000176200001440000000206013773172540016521 0ustar liggesusers R version 3.6.2 (2019-12-12) -- "Dark and Stormy Night" Copyright (C) 2019 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > suppressPackageStartupMessages(library(lwgeom)) > suppressPackageStartupMessages(library(sf)) > nc = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) > nc = st_transform(nc, 3857) > st_perimeter(nc)[1:5] Units: [m] [1] 176215.7 149267.4 199588.2 375140.4 263513.1 > st_perimeter_2d(nc)[1:5] Units: [m] [1] 176215.7 149267.4 199588.2 375140.4 263513.1 > > proc.time() user system elapsed 0.395 0.038 0.423 lwgeom/tests/azimuth.Rout.save0000644000176200001440000000160313773172540016210 0ustar liggesusers R version 3.6.2 (2019-12-12) -- "Dark and Stormy Night" Copyright (C) 2019 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > suppressPackageStartupMessages(library(sf)) > suppressPackageStartupMessages(library(lwgeom)) > p = st_sfc(st_point(c(7,52)), st_point(c(8,53)), crs = 4326) > st_geod_azimuth(p) 0.5410385 [rad] > > proc.time() user system elapsed 0.372 0.015 0.378 lwgeom/tests/perimeter.R0000644000176200001440000000035513773172540015041 0ustar liggesuserssuppressPackageStartupMessages(library(lwgeom)) suppressPackageStartupMessages(library(sf)) nc = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) nc = st_transform(nc, 3857) st_perimeter(nc)[1:5] st_perimeter_2d(nc)[1:5] lwgeom/tests/azimuth.R0000644000176200001440000000025413773172540014524 0ustar liggesuserssuppressPackageStartupMessages(library(sf)) suppressPackageStartupMessages(library(lwgeom)) p = st_sfc(st_point(c(7,52)), st_point(c(8,53)), crs = 4326) st_geod_azimuth(p) lwgeom/tests/twkb.Rout.save0000644000176200001440000000242714027433217015475 0ustar liggesusers R version 4.0.4 (2021-02-15) -- "Lost Library Book" Copyright (C) 2021 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > l = structure(list(as.raw(c(0x02, 0x00, 0x02, 0x02, 0x02, 0x08, 0x08))), class = "TWKB") > suppressPackageStartupMessages(library(sf)) > suppressPackageStartupMessages(library(lwgeom)) > st_as_sfc(l) Geometry set for 1 feature Geometry type: LINESTRING Dimension: XY Bounding box: xmin: 1 ymin: 1 xmax: 5 ymax: 5 CRS: NA LINESTRING (1 1, 5 5) > st_as_sfc(structure(list(l[[1]], l[[1]]), class = "TWKB")) Geometry set for 2 features Geometry type: LINESTRING Dimension: XY Bounding box: xmin: 1 ymin: 1 xmax: 5 ymax: 5 CRS: NA LINESTRING (1 1, 5 5) LINESTRING (1 1, 5 5) > > proc.time() user system elapsed 0.355 0.040 0.389 lwgeom/tests/testthat.Rout.save0000644000176200001440000000212514100240473016351 0ustar liggesusers R version 4.1.0 (2021-05-18) -- "Camp Pontanezen" Copyright (C) 2021 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > library(testthat) > suppressPackageStartupMessages(library(sf)) > suppressPackageStartupMessages(library(lwgeom)) > > test_check("lwgeom") ══ Skipped tests ═══════════════════════════════════════════════════════════════ • empty test (3) [ FAIL 0 | WARN 0 | SKIP 3 | PASS 61 ] > > proc.time() user system elapsed 1.428 0.063 1.485 lwgeom/tests/testthat.R0000644000176200001440000000020413773172540014676 0ustar liggesuserslibrary(testthat) suppressPackageStartupMessages(library(sf)) suppressPackageStartupMessages(library(lwgeom)) test_check("lwgeom") lwgeom/tests/dist.Rout.save0000644000176200001440000000266214100245155015464 0ustar liggesusers R version 4.1.0 (2021-05-18) -- "Camp Pontanezen" Copyright (C) 2021 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > suppressPackageStartupMessages(library(sf)) > library(sp) > suppressPackageStartupMessages(library(units)) > library(geosphere) > > x = st_sfc( + st_point(c(0,0)), + st_point(c(1,0)), + st_point(c(2,0)), + st_point(c(3,0)), + crs = 4326 + ) > > y = st_sfc( + st_point(c(0,10)), + st_point(c(1,0)), + st_point(c(2,0)), + st_point(c(3,0)), + st_point(c(4,0)), + crs = 4326 + ) > > st_crs(y) = 4326 > st_crs(x) = 4326 > sf_use_s2(FALSE) Spherical geometry (s2) switched off > (d.sf = st_distance(x, y)) Units: [m] [,1] [,2] [,3] [,4] [,5] [1,] 1105855 111319.5 222639.0 333958.5 445278.0 [2,] 1111387 0.0 111319.5 222639.0 333958.5 [3,] 1127822 111319.5 0.0 111319.5 222639.0 [4,] 1154693 222639.0 111319.5 0.0 111319.5 > > proc.time() user system elapsed 0.542 0.047 0.581 lwgeom/tests/dist.R0000644000176200001440000000064014100240677013775 0ustar liggesuserssuppressPackageStartupMessages(library(sf)) library(sp) suppressPackageStartupMessages(library(units)) library(geosphere) x = st_sfc( st_point(c(0,0)), st_point(c(1,0)), st_point(c(2,0)), st_point(c(3,0)), crs = 4326 ) y = st_sfc( st_point(c(0,10)), st_point(c(1,0)), st_point(c(2,0)), st_point(c(3,0)), st_point(c(4,0)), crs = 4326 ) st_crs(y) = 4326 st_crs(x) = 4326 sf_use_s2(FALSE) (d.sf = st_distance(x, y)) lwgeom/tests/geod.Rout.save0000644000176200001440000000614514100240464015435 0ustar liggesusers R version 4.1.0 (2021-05-18) -- "Camp Pontanezen" Copyright (C) 2021 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > ### Name: lw_geodetic > ### Title: geodetic length, area, and predicates > ### Aliases: lw_geodetic st_geod_area lw_geodetic st_geod_length > ### lw_geodetic st_geod_segmentize lw_geodetic st_geod_covers > > ### ** Examples > > suppressPackageStartupMessages(library(sf)) > suppressPackageStartupMessages(library(lwgeom)) > suppressPackageStartupMessages(library(units)) > nc = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) > st_geod_area(nc[1:3,]) Units: [m^2] [1] 1137388604 611077263 1423489919 > # st_area(nc[1:3,]) > l = st_sfc(st_linestring(rbind(c(7,52), c(8,53))), crs = 4326) > st_geod_length(l) 130359.3 [m] > pol = st_polygon(list(rbind(c(0,0), c(0,60), c(60,60), c(0,0)))) > x = st_sfc(pol, crs = 4326) > seg = st_geod_segmentize(x[1], set_units(10, km)) > plot(seg, graticule = TRUE, axes = TRUE) > pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) > pt = st_point(c(0,90)) > x = st_sfc(pole, pt, crs = 4326) > st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)]) Sparse geometry binary predicate list of length 3, where the predicate was `covers' 1: 1, 2, 3, 4 2: 1, 2, 3, 4 3: 1, 2, 3, 4 > st_geod_covered_by(x[c(2,2)], x[c(1,1,1)]) Sparse geometry binary predicate list of length 2, where the predicate was `covered_by' 1: 1, 2, 3 2: 1, 2, 3 > st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)], sparse = FALSE) [,1] [,2] [,3] [,4] [1,] TRUE TRUE TRUE TRUE [2,] TRUE TRUE TRUE TRUE [3,] TRUE TRUE TRUE TRUE > st_geod_covered_by(x[c(2,2)], x[c(1,1,1)], sparse = FALSE) [,1] [,2] [,3] [1,] TRUE TRUE TRUE [2,] TRUE TRUE TRUE > > # box crossing the dateline: > #box = st_polygon(list(rbind(c(179.5,0), c(179.5,1), c(-179.5,1), c(-179.5,0), c(179.5,0)))) > box = st_polygon(list(rbind(c(179.5,0.1), c(179.5,1), c(-179.5,1), c(-179.5,0.1), c(179.5,0.1)))) > b = st_sfc(box, crs = 4326) > units::set_units(st_geod_area(b), km^2) # approx 111^2 11077.84 [km^2] > > pt = st_point(c(30, 70)) > x = st_sfc(pole, pt, pt, crs = 4326) > st_geod_distance(x, x) Units: [m] [,1] [,2] [,3] [1,] 0 1378923 1378923 [2,] 1378923 0 0 [3,] 1378923 0 0 > st_geod_distance(x, x, sparse = TRUE) Sparse geometry binary predicate list of length 3, where the predicate was `st_is_within_distance' 1: 1 2: 2, 3 3: 2, 3 > st_geod_distance(x, x, tolerance = 1, sparse = TRUE) Sparse geometry binary predicate list of length 3, where the predicate was `st_is_within_distance' 1: 1 2: 2, 3 3: 2, 3 > > proc.time() user system elapsed 0.525 0.035 0.553 lwgeom/tests/twkb.R0000644000176200001440000000037513773172540014016 0ustar liggesusersl = structure(list(as.raw(c(0x02, 0x00, 0x02, 0x02, 0x02, 0x08, 0x08))), class = "TWKB") suppressPackageStartupMessages(library(sf)) suppressPackageStartupMessages(library(lwgeom)) st_as_sfc(l) st_as_sfc(structure(list(l[[1]], l[[1]]), class = "TWKB")) lwgeom/configure.ac0000644000176200001440000003327214332732450014044 0ustar liggesusersdnl Process this file with autoconf to produce a configure script. dnl GDAL stuff largely copied from rgdal, (c) Roger Bivand AC_INIT AC_CONFIG_SRCDIR([src/lwgeom.cpp]) : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then echo "could not determine R_HOME" exit 1 fi RBIN="${R_HOME}/bin/R" # pick all flags for testing from R : ${CC=`"${RBIN}" CMD config CC`} : ${CXX=`"${RBIN}" CMD config CXX`} : ${CFLAGS=`"${RBIN}" CMD config CFLAGS`} : ${CPPFLAGS=`"${RBIN}" CMD config CPPFLAGS`} : ${LDFLAGS=`"${RBIN}" CMD config LDFLAGS`} # AC_SUBST([CC],["clang"]) # AC_SUBST([CXX],["clang++"]) AC_MSG_NOTICE([CC: ${CC}]) AC_MSG_NOTICE([CXX: ${CXX}]) # # PROJ # PROJ_CONFIG="pkg-config proj" if `$PROJ_CONFIG --exists` ; then AC_MSG_NOTICE([pkg-config proj exists, will use it]) proj_config_ok=yes else proj_config_ok=no fi AC_ARG_WITH([proj-include], AS_HELP_STRING([--with-proj-include=DIR],[location of proj header files]), [proj_include_path=$withval]) if test [ -n "$proj_include_path" ] ; then AC_SUBST([PROJ_CPPFLAGS],["-I${proj_include_path}"]) else if test "${proj_config_ok}" = yes; then PROJ_INCLUDE_PATH=`${PROJ_CONFIG} --cflags` AC_SUBST([PROJ_CPPFLAGS],["${PROJ_INCLUDE_PATH}"]) fi fi # honor PKG_xx overrides # for CPPFLAGS we will superfluously double R's flags # since we'll set PKG_CPPFLAGS with this, but that shouldn't hurt AC_ARG_WITH([proj-api], AS_HELP_STRING([--with-proj-api=yes/no],[use the deprecated proj_api.h even when PROJ 6 is available; default no]), [proj_api=$withval]) PROJ6="no" PROJH="no" if test "${proj_config_ok}" = yes; then PROJ_VERSION=`${PROJ_CONFIG} --modversion` PROJV1=`echo "${PROJ_VERSION}" | cut -c 1` if test "${PROJV1}" -ge 5; then PROJ6="yes" PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DHAVE_PROJ_H" if test "${proj_api}" = yes; then AC_MSG_NOTICE([using proj_api.h even with PROJ 5/6]) PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" else AC_MSG_NOTICE([using proj.h.]) PROJH="yes" fi fi else if test "${PROJH}" = no ; then PROJH=yes AC_CHECK_HEADERS(proj.h,,PROJH=no) if test "${PROJH}" = yes; then PROJ6="yes" PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DHAVE_PROJ_H" fi fi fi CPPFLAGS="${INCPPFLAGS} ${PKG_CPPFLAGS} ${PROJ_CPPFLAGS}" # see https://github.com/r-spatial/lwgeom/issues/28 #if test "${PROJH}" = no #then # proj4ok=yes # AC_CHECK_HEADERS(proj_api.h,,proj4ok=no) # if test "${proj4ok}" = no; then # AC_MSG_ERROR([proj_api.h not found in standard or given locations.]) # fi #fi # dnl ditto for a library path AC_ARG_WITH([proj-lib], AS_HELP_STRING([--with-proj-lib=LIB_PATH],[the location of proj libraries]), [proj_lib_path=$withval]) if test [ -n "$proj_lib_path" ] ; then AC_SUBST([PROJ_LIBS], ["-L${proj_lib_path} ${INPKG_LIBS} -lproj"]) else if test "${proj_config_ok}" = yes; then if test `uname` = "Darwin"; then PROJ_LIB_PATH=`${PROJ_CONFIG} --libs --static` else PROJ_LIB_PATH=`${PROJ_CONFIG} --libs` fi AC_SUBST([PROJ_LIBS], ["${PROJ_LIB_PATH} ${INPKG_LIBS}"]) proj_version=`${PROJ_CONFIG} --modversion` AC_MSG_NOTICE([PROJ: ${proj_version}]) else PROJ_LIBS="${PKG_LIBS} -lproj" fi fi LIBS="${PROJ_LIBS} ${INLIBS} ${PKG_LIBS}" if test "${PROJH}" = no; then proj4ok=yes AC_CHECK_LIB(proj,pj_init_plus,,proj4ok=no) if test "${proj4ok}" = no; then AC_MSG_ERROR([libproj not found in standard or given locations.]) fi [cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { printf("%d\n", PJ_VERSION); exit(0); } _EOCONF] else [cat > proj_conf_test.cpp <<_EOCONF #include #include #include int main(void) { proj_context_create(); exit(0); } _EOCONF] #AC_CHECK_LIB(proj,proj_context_create,,proj6ok=no) AC_MSG_CHECKING(PROJ: checking whether PROJ and sqlite3 are available for linking:) ${CXX} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.cpp ${LIBS} -lsqlite3 2> errors.txt if test `echo $?` -ne 0 ; then proj6ok=no AC_MSG_RESULT(no) else proj6ok=yes AC_MSG_RESULT(yes) fi if test "${proj6ok}" = no; then AC_MSG_ERROR([libproj not found in standard or given locations.]) fi [cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { printf("%d.%d.%d\n", PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH); exit(0); } _EOCONF] fi #AC_MSG_NOTICE([PKG_LIBS: ${PKG_LIBS}]) ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} proj_version=`./proj_conf_test` AC_ARG_WITH([proj-share], AS_HELP_STRING([--with-proj-share=SHARE_PATH],[the location of proj metadata files]), [proj_share_path=$withval]) if test [ -n "$proj_share_path" ] ; then AC_MSG_NOTICE([PROJ_LIB: ${proj_share_path}]) fi if test ${PROJ6} = "no"; then [cat > proj_conf_test.c <<_EOCONF #include #include #include #if PJ_VERSION <= 480 FILE *pj_open_lib(projCtx, const char *, const char *); #endif int main(void) { #if PJ_VERSION <= 480 FILE *fp; #else PAFile fp; #endif projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "epsg", "rb"); if (fp == NULL) exit(1); #if PJ_VERSION <= 480 fclose(fp); #else pj_ctx_fclose(ctx, fp); #endif exit(0); } _EOCONF] ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test [ -n "$proj_share_path" ] ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi AC_MSG_CHECKING(PROJ: epsg found and readable) if test ${proj_share} -eq 1 ; then AC_MSG_RESULT(no) STOP="stop" else AC_MSG_RESULT(yes) fi rm -f proj_conf_test.c proj_conf_test if test "$STOP" = "stop" ; then echo "Error: proj/epsg not found" echo "Either install missing proj support files, for example" echo "the proj-nad and proj-epsg RPMs on systems using RPMs," echo "or if installed but not autodetected, set PROJ_LIB to the" echo "correct path, and if need be use the --with-proj-share=" echo "configure argument." exit 1 fi else # proj >= 6 if test "${PROJH}" = no; then [cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { PAFile fp; projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "proj.db", "rb"); if (fp == NULL) exit(1); pj_ctx_fclose(ctx, fp); exit(0); } _EOCONF] ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test [ -n "$proj_share_path" ] ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi AC_MSG_CHECKING(PROJ: proj.db found and readable) if test ${proj_share} -eq 1 ; then AC_MSG_RESULT(no) STOP="stop" else AC_MSG_RESULT(yes) fi rm -f proj_conf_test.c proj_conf_test if test "$STOP" = "stop" ; then echo "Error: proj/proj.db not found" echo "Either install missing proj support files, set PROJ_LIB to the" echo "correct path, and if need be use the --with-proj-share=" echo "configure argument." exit 1 fi [cat > proj_conf_test.c <<_EOCONF #include #include #include #if PJ_VERSION <= 480 FILE *pj_open_lib(projCtx, const char *, const char *); #endif int main(void) { #if PJ_VERSION <= 480 FILE *fp; #else PAFile fp; #endif projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "conus", "rb"); if (fp == NULL) exit(1); #if PJ_VERSION <= 480 fclose(fp); #else pj_ctx_fclose(ctx, fp); #endif exit(0); } _EOCONF] ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test [ -n "$proj_share_path" ] ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi AC_MSG_CHECKING(PROJ: conus found and readable) if test ${proj_share} -eq 1 ; then WARN="warn" AC_MSG_RESULT(no) else AC_MSG_RESULT(yes) fi rm -f proj_conf_test.c proj_conf_test if test "$WARN" = "warn" ; then echo "Note: proj/conus not found" echo "No support available in PROJ4 for NAD grid datum transformations" echo "If required, consider re-installing from source with the contents" echo "of proj-datumgrid-1..zip from http://download.osgeo.org/proj/ in nad/." fi fi # PROJH = no fi # proj >= 6 # # POSTGIS/PROJ version: # [cat > proj_conf_test.c <<_EOCONF #include #ifdef HAVE_PROJ_H #include int main(void) { printf("%d%d%d\n", PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH); return 0; } #else #include int main(void) { printf("%d\n", PJ_VERSION); return 0; } #endif _EOCONF] # AC_MSG_NOTICE([PKG_LIBS: ${PKG_LIBS}]) ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} proj_version=`./proj_conf_test` POSTGIS_PROJ_VERSION=`echo $proj_version | cut -c "1,2"` AC_MSG_NOTICE([POSTGIS_PROJ_VERSION: ${POSTGIS_PROJ_VERSION}]) #AC_SUBST([POSTGIS_PROJ_VERSION],["${PROJV12}"]) #AC_DEFINE_UNQUOTED([POSTGIS_PROJ_VERSION], [$POSTGIS_PROJ_VERSION], [PROJ library version]) AC_SUBST(POSTGIS_PROJ_VERSION) # xxx if test ${POSTGIS_PROJ_VERSION} -lt 60 ; then PKG_CPPFLAGS="${PKG_CPPFLAGS} -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" else PKG_CPPFLAGS="${PKG_CPPFLAGS} -DUSE_PROJ_H" fi # # GEOS: # GEOS_CONFIG="geos-config" GEOS_CONFIG_SET="no" AC_ARG_WITH([geos-config], AS_HELP_STRING([--with-geos-config=GEOS_CONFIG],[the location of geos-config]), [geos_config=$withval]) if test [ -n "$geos_config" ] ; then GEOS_CONFIG_SET="yes" AC_SUBST([GEOS_CONFIG],["${geos_config}"]) AC_MSG_NOTICE(geos-config set to $GEOS_CONFIG) fi if test ["$GEOS_CONFIG_SET" = "no"] ; then AC_PATH_PROG([GEOS_CONFIG], ["$GEOS_CONFIG"],["no"]) if test ["$GEOS_CONFIG" = "no"] ; then AC_MSG_RESULT(no) AC_MSG_ERROR([geos-config not found or not executable.]) fi else AC_MSG_CHECKING(geos-config exists) if test -r "${GEOS_CONFIG}"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_ERROR([geos-config not found - configure argument error.]) fi AC_MSG_CHECKING(geos-config executable) if test -x "${GEOS_CONFIG}"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_ERROR([geos-config not executable.]) fi fi AC_MSG_CHECKING(geos-config usability) if test `${GEOS_CONFIG} --version` then GEOS_CLIBS=`${GEOS_CONFIG} --clibs` #GEOS_DEP_CLIBS=`geos-config --static-clibs` -- this gives -m instead of -lm, which breaks clang # fixed in 3.7.0 at https://github.com/libgeos/libgeos/pull/73#issuecomment-262208677 GEOS_DEP_CLIBS=`${GEOS_CONFIG} --static-clibs | sed 's/-m/-lm/g'` GEOS_CPPFLAGS=`${GEOS_CONFIG} --cflags` AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_ERROR([${GEOS_CONFIG} not usable]) fi GEOS_VERSION=`${GEOS_CONFIG} --version` AC_MSG_NOTICE([GEOS: ${GEOS_VERSION}]) AC_MSG_CHECKING([GEOS version >= 3.5.0]) # PostGIS 3.0.0 requires this GEOS_VER_DOT=`echo $GEOS_VERSION | tr -d "."` if test ${GEOS_VER_DOT} -lt 350 ; then AC_MSG_RESULT(no) AC_MSG_ERROR([upgrade GEOS to 3.5.0 or later]) else AC_MSG_RESULT(yes) fi # honor PKG_xx overrides # for CPPFLAGS we will superfluously double R's flags # since we'll set PKG_CPPFLAGS with this, but that shouldn't hurt PKG_CPPFLAGS="${PKG_CPPFLAGS} ${GEOS_CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PKG_CPPFLAGS}" LIBS="${LIBS} ${PKG_LIBS} ${GEOS_CLIBS}" AC_SUBST([PKG_CPPFLAGS], ["${PKG_CPPFLAGS} -DPOSTGIS_GEOS_VERSION=35"]) #geosok=yes #AC_CHECK_HEADERS(geos_c.h,,geosok=no) #if test "${geosok}" = no; then # AC_MSG_ERROR([geos_c.h not found in given locations.]) #fi [cat > geos_test.cpp <<_EOCONF #include #ifdef __cplusplus extern "C" { #endif static void __errorHandler(const char *fmt, ...) { return; } static void __warningHandler(const char *fmt, ...) { return; } int main(void) { GEOSContextHandle_t r = initGEOS_r((GEOSMessageHandler) __warningHandler, (GEOSMessageHandler) __errorHandler); finishGEOS_r(r); } #ifdef __cplusplus } #endif _EOCONF] #echo "${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${LIBS}" AC_MSG_CHECKING(geos: linking with ${GEOS_CLIBS}) ${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${GEOS_CLIBS} 2> errors.txt if test `echo $?` -ne 0 ; then geosok=no AC_MSG_RESULT(no) else AC_SUBST([GEOS_LIBS], ["${GEOS_CLIBS}"]) AC_MSG_RESULT(yes) fi if test "${geosok}" = no; then AC_MSG_CHECKING(geos: linking with ${GEOS_DEP_CLIBS}) ${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${GEOS_DEP_CLIBS} 2> errors.txt if test `echo $?` -ne 0 ; then geosok=no AC_MSG_RESULT(no) cat errors.txt AC_MSG_NOTICE([Install failure: compilation and/or linkage problems.]) AC_MSG_ERROR([GEOS_init_r not found in libgeos_c.]) else AC_SUBST([GEOS_LIBS], ["${GEOS_DEP_CLIBS}"]) AC_MSG_RESULT(yes) fi fi rm -f geos_test errors.txt geos_test.cpp # # liblwgeom # AC_SUBST([OBJECTS], ["${OBJECTS} \$(OBJECTS_RCPP)"]) #AC_CHECK_LIB(proj, pj_init_plus, # AC_SUBST([PKG_LIBS], ["${PKG_LIBS} -lproj"]), # AC_MSG_ERROR([libproj not found a standard locations.])) AC_CHECK_LIB(geos_c, GEOS_init_r, AC_SUBST([PKG_LIBS], ["${LIBS} -lgeos_c"]), AC_SUBST([PKG_LIBS], ["${LIBS} ${PROJ_LIBS} ${GEOS_LIBS}"])) AC_SUBST([OBJECTS], ["${OBJECTS} \$(OBJECTS_LIBLWGEOM)"]) # Must keep the leading ${CPPFLAGS} or the previous CPPFLAGS don't get saved AC_SUBST([PKG_CPPFLAGS], ["${CPPFLAGS} ${PKG_CPPFLAGS} -I./liblwgeom -DHAVE_LIBGEOM_INTERNAL_H"]) # # concluding substitution # AC_MSG_NOTICE([Package CPP flags: ${PKG_CPPFLAGS}]) AC_MSG_NOTICE([Package LIBS: ${LIBS}]) AC_CONFIG_FILES(src/Makevars src/postgis_config.h) AC_OUTPUT lwgeom/src/0000755000176200001440000000000014432625425012342 5ustar liggesuserslwgeom/src/sub.cpp0000644000176200001440000000156313773172540013646 0ustar liggesusers#include #include extern "C" { #include } using namespace Rcpp; #include "lwgeom.h" // sfc <--> lwgeom // [[Rcpp::export]] Rcpp::List CPL_linesubstring(Rcpp::List sfc, double from, double to, double tolerance = 0.0) { std::vector lw = lwgeom_from_sfc(sfc); std::vector out(sfc.size()); for (size_t i = 0; i < lw.size(); i++) { // LWLINE *iline = lwgeom_as_lwline(lw[i]); if ( lw[i]->type == LINETYPE ) { POINTARRAY *opa; opa = ptarray_substring(((LWLINE*)lw[i])->points, from, to, tolerance); if ( opa->npoints == 1 ) /* Point returned */ out[i] = (LWGEOM *)lwpoint_construct(lw[i]->srid, NULL, opa); // #nocov else out[i] = (LWGEOM *)lwline_construct(lw[i]->srid, NULL, opa); } else stop("geometry should be of LINE type"); // #nocov lwgeom_free(lw[i]); } return sfc_from_lwgeom(out); } lwgeom/src/README0000644000176200001440000000112613773172540013224 0ustar liggesusersliblwgeom code obtained from from https://github.com/postgis/postgis git commit d9a98c5be3c8644699220729f11ed48488548bad configured with: ./configure --without-pgconfig --without-sfcgal --without-libiconv-prefix --without-libintl-prefix --without-json --without-protobuf --without-topology --without-raster removed: *_sfcgal.[ch] all diffs, registered with "diff -c | grep -v 'Only in'", are found in file DIFFS In addition: postgis_version.h, outcomment line 139 which defines POSTGIS_GEOS_VERSION; ./configure now does this on the command line. in addition: "%.*g" -> "%*g" in lwgeom_wkt.c lwgeom/src/DIFFS0000644000176200001440000002147213773172540013130 0ustar liggesusersdiff -c liblwgeom/kmeans.c ../../postgis/liblwgeom/kmeans.c *** liblwgeom/kmeans.c 2017-11-10 22:45:06.065006363 +0100 --- ../../postgis/liblwgeom/kmeans.c 2017-10-20 12:30:08.754503045 +0200 *************** *** 137,144 **** rc = pthread_create(&thread[i], &thread_attr, update_r_threaded_main, (void *) &thread_config[i]); if (rc) { ! /* printf("ERROR: return code from pthread_create() is %d\n", rc); */ ! /* exit(-1); */ } } --- 137,144 ---- rc = pthread_create(&thread[i], &thread_attr, update_r_threaded_main, (void *) &thread_config[i]); if (rc) { ! printf("ERROR: return code from pthread_create() is %d\n", rc); ! exit(-1); } } *************** *** 152,159 **** rc = pthread_join(thread[i], &status); if (rc) { ! /* printf("ERROR: return code from pthread_join() is %d\n", rc); ! exit(-1); */ } } } --- 152,159 ---- rc = pthread_join(thread[i], &status); if (rc) { ! printf("ERROR: return code from pthread_join() is %d\n", rc); ! exit(-1); } } } *************** *** 220,227 **** rc = pthread_create(&thread[i], &thread_attr, update_means_threaded_main, (void *) config); if (rc) { ! /* printf("ERROR: return code from pthread_create() is %d\n", rc); ! exit(-1); */ } } --- 220,227 ---- rc = pthread_create(&thread[i], &thread_attr, update_means_threaded_main, (void *) config); if (rc) { ! printf("ERROR: return code from pthread_create() is %d\n", rc); ! exit(-1); } } *************** *** 234,241 **** rc = pthread_join(thread[i], &status); if (rc) { ! /* printf("ERROR: return code from pthread_join() is %d\n", rc); ! exit(-1); */ } } --- 234,241 ---- rc = pthread_join(thread[i], &status); if (rc) { ! printf("ERROR: return code from pthread_join() is %d\n", rc); ! exit(-1); } } diff -c liblwgeom/lwgeodetic_tree.c ../../postgis/liblwgeom/lwgeodetic_tree.c *** liblwgeom/lwgeodetic_tree.c 2017-11-10 22:39:40.653760871 +0100 --- ../../postgis/liblwgeom/lwgeodetic_tree.c 2017-10-20 12:30:08.754503045 +0200 *************** *** 789,795 **** void circ_tree_print(const CIRC_NODE* node, int depth) { - /* int i; if (circ_node_is_leaf(node)) --- 789,794 ---- *************** *** 833,839 **** { circ_tree_print(node->nodes[i], depth + 1); } - */ return; } --- 832,837 ---- diff -c liblwgeom/lwgeom_geos.c ../../postgis/liblwgeom/lwgeom_geos.c *** liblwgeom/lwgeom_geos.c 2017-11-11 09:47:18.923276493 +0100 --- ../../postgis/liblwgeom/lwgeom_geos.c 2017-10-20 12:30:08.754503045 +0200 *************** *** 23,29 **** * **********************************************************************/ - #include "R.h" /* unif_rand() to replace rand() */ #include "lwgeom_geos.h" #include "liblwgeom.h" --- 23,28 ---- *************** *** 1771,1777 **** mpt = lwmpoint_construct_empty(srid, 0, 0); /* Init random number generator */ ! /* srand(time(NULL)); */ /* Now we fill in an array of cells, and then shuffle that array, */ /* so we can visit the cells in random order to avoid visual ugliness */ --- 1770,1776 ---- mpt = lwmpoint_construct_empty(srid, 0, 0); /* Init random number generator */ ! srand(time(NULL)); /* Now we fill in an array of cells, and then shuffle that array, */ /* so we can visit the cells in random order to avoid visual ugliness */ *************** *** 1791,1801 **** n = sample_height*sample_width; if (n > 1) { for (i = 0; i < n - 1; ++i) { ! /* size_t rnd = (size_t) rand(); ! size_t j = i + rnd / (RAND_MAX / (n - i) + 1); */ ! ! size_t rnd = (size_t) unif_rand(); ! size_t j = i + rnd / (1.0 / (n - i) + 1); memcpy(tmp, (char *)cells + j * stride, size); memcpy((char *)cells + j * stride, (char *)cells + i * stride, size); --- 1790,1797 ---- n = sample_height*sample_width; if (n > 1) { for (i = 0; i < n - 1; ++i) { ! size_t rnd = (size_t) rand(); ! size_t j = i + rnd / (RAND_MAX / (n - i) + 1); memcpy(tmp, (char *)cells + j * stride, size); memcpy((char *)cells + j * stride, (char *)cells + i * stride, size); *************** *** 1814,1823 **** int contains = 0; double y = bbox.ymin + cells[2*i] * sample_cell_size; double x = bbox.xmin + cells[2*i+1] * sample_cell_size; ! /* x += rand() * sample_cell_size / RAND_MAX; ! y += rand() * sample_cell_size / RAND_MAX; */ ! x += unif_rand() * sample_cell_size; ! y += unif_rand() * sample_cell_size; if (x >= bbox.xmax || y >= bbox.ymax) continue; --- 1810,1817 ---- int contains = 0; double y = bbox.ymin + cells[2*i] * sample_cell_size; double x = bbox.xmin + cells[2*i+1] * sample_cell_size; ! x += rand() * sample_cell_size / RAND_MAX; ! y += rand() * sample_cell_size / RAND_MAX; if (x >= bbox.xmax || y >= bbox.ymax) continue; diff -c liblwgeom/lwin_wkt_lex.c ../../postgis/liblwgeom/lwin_wkt_lex.c *** liblwgeom/lwin_wkt_lex.c 2017-11-15 12:24:21.433081247 +0100 --- ../../postgis/liblwgeom/lwin_wkt_lex.c 2017-10-20 12:30:08.758503081 +0200 *************** *** 787,794 **** /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ ! /* #define ECHO do { if (fwrite( wkt_yytext, wkt_yyleng, 1, wkt_yyout )) {} } while (0) */ ! #define ECHO #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, --- 787,793 ---- /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ ! #define ECHO do { if (fwrite( wkt_yytext, wkt_yyleng, 1, wkt_yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, *************** *** 900,907 **** if ( ! wkt_yyin ) wkt_yyin = stdin; ! /* if ( ! wkt_yyout ) ! wkt_yyout = stdout; */ if ( ! YY_CURRENT_BUFFER ) { wkt_yyensure_buffer_stack (); --- 899,906 ---- if ( ! wkt_yyin ) wkt_yyin = stdin; ! if ( ! wkt_yyout ) ! wkt_yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { wkt_yyensure_buffer_stack (); *************** *** 1869,1875 **** static void yy_fatal_error (yyconst char* msg ) { ! /* (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); */ } /* Redefine yyless() so it works in section 3 code. */ --- 1868,1875 ---- static void yy_fatal_error (yyconst char* msg ) { ! (void) fprintf( stderr, "%s\n", msg ); ! exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ *************** *** 1985,1991 **** /* Defined in main.c */ #ifdef YY_STDINIT wkt_yyin = stdin; ! /* wkt_yyout = stdout; */ #else wkt_yyin = (FILE *) 0; wkt_yyout = (FILE *) 0; --- 1985,1991 ---- /* Defined in main.c */ #ifdef YY_STDINIT wkt_yyin = stdin; ! wkt_yyout = stdout; #else wkt_yyin = (FILE *) 0; wkt_yyout = (FILE *) 0; diff -c liblwgeom/lwout_x3d.c ../../postgis/liblwgeom/lwout_x3d.c *** liblwgeom/lwout_x3d.c 2017-11-11 10:13:54.774661432 +0100 --- ../../postgis/liblwgeom/lwout_x3d.c 2017-10-20 12:30:08.762503117 +0200 *************** *** 807,814 **** else lwerror("asx3d3_collection_buf: unknown geometry type"); ! /* ptr += printf(ptr, ""); EJP, no clue if this was intended: */ ! ptr += sprintf(ptr, ""); } /* Close outmost tag */ --- 807,813 ---- else lwerror("asx3d3_collection_buf: unknown geometry type"); ! ptr += printf(ptr, ""); } /* Close outmost tag */ diff -c liblwgeom/lwutil.c ../../postgis/liblwgeom/lwutil.c *** liblwgeom/lwutil.c 2017-11-10 22:41:03.504970943 +0100 --- ../../postgis/liblwgeom/lwutil.c 2017-10-20 12:30:08.762503117 +0200 *************** *** 119,125 **** char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! /* fprintf(stderr, "%s\n", msg); */ } static void --- 119,125 ---- char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! fprintf(stderr, "%s\n", msg); } static void *************** *** 134,140 **** msg[i] = ' '; vsnprintf(msg+i, LW_MSG_MAXLEN-i, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! /* fprintf(stderr, "%s\n", msg); */ } } --- 134,140 ---- msg[i] = ' '; vsnprintf(msg+i, LW_MSG_MAXLEN-i, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! fprintf(stderr, "%s\n", msg); } } *************** *** 144,150 **** char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! /* fprintf(stderr, "%s\n", msg); exit(1); */ } /** --- 144,151 ---- char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; ! fprintf(stderr, "%s\n", msg); ! exit(1); } /** lwgeom/src/geos.cpp0000644000176200001440000000035013773172540014003 0ustar liggesusers#define GEOS_USE_ONLY_R_API // prevents using non-thread-safe GEOSxx functions without _r extension. #include #include // [[Rcpp::export]] std::string CPL_geos_version(bool b = false) { return GEOS_VERSION; } lwgeom/src/postgis_config.h.in0000644000176200001440000001044713773172540016145 0ustar liggesusers/* postgis_config.h. Generated from postgis_config.h.in by configure. */ #ifndef POSTGIS_CONFIG_H #define POSTGIS_CONFIG_H 1 #include "postgis_svn_revision.h" /* Manually manipulate the POSTGIS_DEBUG_LEVEL, it is not affected by the configure process */ #define POSTGIS_DEBUG_LEVEL 0 /* Define to 1 to enable memory checks in pointarray management. */ #define PARANOIA_LEVEL 0 /* Define to 1 if translation of program messages to the user's native language is requested. */ #define ENABLE_NLS 1 /* Define for some functions we are interested in */ #define HAVE_FSEEKO 1 /* Define if the GNU gettext() function is already present or preinstalled. */ #define HAVE_GETTEXT 1 /* Define if the build is big endian */ /* #undef WORDS_BIGENDIAN */ /* Define if you have the iconv() function and it works. */ #define HAVE_ICONV 1 /* Define to 1 if you have the `iconvctl' function. */ /* #undef HAVE_ICONVCTL */ /* ieeefp.h header */ #define HAVE_IEEEFP_H 0 /* Define to 1 if you have the `geos_c' library (-lgeos_c). */ #define HAVE_LIBGEOS_C 1 /* Define to 1 if you have the `libiconvctl' function. */ /* #undef HAVE_LIBICONVCTL */ /* Define to 1 if libprotobuf-c is present */ /* #undef HAVE_LIBPROTOBUF */ /* Define to 1 if protobuf_c_version() is present */ /* #undef HAVE_PROTOBUF_C_VERSION */ /* Numeric version number for libprotobuf */ /* #undef LIBPROTOBUF_VERSION */ /* Define to 1 if libprotobuf-c is >= version 1.1 */ /* #undef HAVE_GEOBUF */ /* Define to 1 if libjson is present */ #define HAVE_LIBJSON 1 /* Define to 1 if you have the `pq' library (-lpq). */ #define HAVE_LIBPQ 1 /* Define to 1 if you have the `proj' library (-lproj). */ #define HAVE_LIBPROJ 1 /* Define to 1 if you have the `xml2' library (-lxml2). */ #define HAVE_LIBXML2 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_PARSER_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_TREE_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATHINTERNALS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATH_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if wagyu is being built */ #define HAVE_WAGYU 1 /* Define to 1 if sfcgal is being built */ /* #undef HAVE_SFCGAL */ /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Location of PostgreSQL locale directory */ #define PGSQL_LOCALEDIR "/usr/share/locale" /* PostGIS build date */ #define POSTGIS_BUILD_DATE "2020-01-28 17:06:21" /* SFCGAL library version at buil time */ /* #undef POSTGIS_SFCGAL_VERSION */ /* GDAL library version */ /* #undef POSTGIS_GDAL_VERSION */ /* GEOS library version */ /* #define POSTGIS_GEOS_VERSION 38 */ /* PostGIS libxml2 version */ #define POSTGIS_LIBXML2_VERSION "2.9.4" /* PostGIS library version */ #define POSTGIS_LIB_VERSION "3.0.0" /* PostGIS major version */ #define POSTGIS_MAJOR_VERSION "3" /* PostGIS minor version */ #define POSTGIS_MINOR_VERSION "0" /* PostGIS micro version */ #define POSTGIS_MICRO_VERSION "0" /* PostgreSQL server version */ #define POSTGIS_PGSQL_VERSION 100 /* PROJ library version */ #define POSTGIS_PROJ_VERSION @POSTGIS_PROJ_VERSION@ /* PostGIS Raster build date */ /* #undef POSTGIS_RASTER_BUILD_DATE */ /* PostGIS Raster library version */ /* #undef POSTGIS_RASTER_LIB_VERSION */ /* PostGIS Raster major version */ /* #undef POSTGIS_RASTER_MAJOR_VERSION */ /* PostGIS Raster micro version */ /* #undef POSTGIS_RASTER_MICRO_VERSION */ /* PostGIS Raster minor version */ /* #undef POSTGIS_RASTER_MINOR_VERSION */ /* PostGIS Raster scripts version */ /* #undef POSTGIS_RASTER_SCRIPTS_VERSION */ /* PostGIS Raster version */ /* #undef POSTGIS_RASTER_VERSION */ /* Define to 1 if a warning is outputted every time a double is truncated */ /* #undef POSTGIS_RASTER_WARN_ON_TRUNCATION */ /* PostGIS scripts version */ #define POSTGIS_SCRIPTS_VERSION "3.0.0" /* PostGIS version */ #define POSTGIS_VERSION "3.0 USE_GEOS=1 USE_PROJ=1 USE_STATS=1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ #define YYTEXT_POINTER 1 #endif /* POSTGIS_CONFIG_H */ lwgeom/src/postgis_config.h0000644000176200001440000001042314432613502015521 0ustar liggesusers/* postgis_config.h. Generated from postgis_config.h.in by configure. */ #ifndef POSTGIS_CONFIG_H #define POSTGIS_CONFIG_H 1 #include "postgis_svn_revision.h" /* Manually manipulate the POSTGIS_DEBUG_LEVEL, it is not affected by the configure process */ #define POSTGIS_DEBUG_LEVEL 0 /* Define to 1 to enable memory checks in pointarray management. */ #define PARANOIA_LEVEL 0 /* Define to 1 if translation of program messages to the user's native language is requested. */ #define ENABLE_NLS 1 /* Define for some functions we are interested in */ #define HAVE_FSEEKO 1 /* Define if the GNU gettext() function is already present or preinstalled. */ #define HAVE_GETTEXT 1 /* Define if the build is big endian */ /* #undef WORDS_BIGENDIAN */ /* Define if you have the iconv() function and it works. */ #define HAVE_ICONV 1 /* Define to 1 if you have the `iconvctl' function. */ /* #undef HAVE_ICONVCTL */ /* ieeefp.h header */ #define HAVE_IEEEFP_H 0 /* Define to 1 if you have the `geos_c' library (-lgeos_c). */ #define HAVE_LIBGEOS_C 1 /* Define to 1 if you have the `libiconvctl' function. */ /* #undef HAVE_LIBICONVCTL */ /* Define to 1 if libprotobuf-c is present */ /* #undef HAVE_LIBPROTOBUF */ /* Define to 1 if protobuf_c_version() is present */ /* #undef HAVE_PROTOBUF_C_VERSION */ /* Numeric version number for libprotobuf */ /* #undef LIBPROTOBUF_VERSION */ /* Define to 1 if libprotobuf-c is >= version 1.1 */ /* #undef HAVE_GEOBUF */ /* Define to 1 if libjson is present */ #define HAVE_LIBJSON 1 /* Define to 1 if you have the `pq' library (-lpq). */ #define HAVE_LIBPQ 1 /* Define to 1 if you have the `proj' library (-lproj). */ #define HAVE_LIBPROJ 1 /* Define to 1 if you have the `xml2' library (-lxml2). */ #define HAVE_LIBXML2 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_PARSER_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_TREE_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATHINTERNALS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATH_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if wagyu is being built */ #define HAVE_WAGYU 1 /* Define to 1 if sfcgal is being built */ /* #undef HAVE_SFCGAL */ /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Location of PostgreSQL locale directory */ #define PGSQL_LOCALEDIR "/usr/share/locale" /* PostGIS build date */ #define POSTGIS_BUILD_DATE "2020-01-28 17:06:21" /* SFCGAL library version at buil time */ /* #undef POSTGIS_SFCGAL_VERSION */ /* GDAL library version */ /* #undef POSTGIS_GDAL_VERSION */ /* GEOS library version */ /* #define POSTGIS_GEOS_VERSION 38 */ /* PostGIS libxml2 version */ #define POSTGIS_LIBXML2_VERSION "2.9.4" /* PostGIS library version */ #define POSTGIS_LIB_VERSION "3.0.0" /* PostGIS major version */ #define POSTGIS_MAJOR_VERSION "3" /* PostGIS minor version */ #define POSTGIS_MINOR_VERSION "0" /* PostGIS micro version */ #define POSTGIS_MICRO_VERSION "0" /* PostgreSQL server version */ #define POSTGIS_PGSQL_VERSION 100 /* PROJ library version */ #define POSTGIS_PROJ_VERSION 91 /* PostGIS Raster build date */ /* #undef POSTGIS_RASTER_BUILD_DATE */ /* PostGIS Raster library version */ /* #undef POSTGIS_RASTER_LIB_VERSION */ /* PostGIS Raster major version */ /* #undef POSTGIS_RASTER_MAJOR_VERSION */ /* PostGIS Raster micro version */ /* #undef POSTGIS_RASTER_MICRO_VERSION */ /* PostGIS Raster minor version */ /* #undef POSTGIS_RASTER_MINOR_VERSION */ /* PostGIS Raster scripts version */ /* #undef POSTGIS_RASTER_SCRIPTS_VERSION */ /* PostGIS Raster version */ /* #undef POSTGIS_RASTER_VERSION */ /* Define to 1 if a warning is outputted every time a double is truncated */ /* #undef POSTGIS_RASTER_WARN_ON_TRUNCATION */ /* PostGIS scripts version */ #define POSTGIS_SCRIPTS_VERSION "3.0.0" /* PostGIS version */ #define POSTGIS_VERSION "3.0 USE_GEOS=1 USE_PROJ=1 USE_STATS=1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ #define YYTEXT_POINTER 1 #endif /* POSTGIS_CONFIG_H */ lwgeom/src/lwgeom.h0000644000176200001440000000016313773172540014007 0ustar liggesusersstd::vector lwgeom_from_sfc(Rcpp::List sfc); Rcpp::List sfc_from_lwgeom(std::vector lwgeom_v); lwgeom/src/postgis_svn_revision.h0000644000176200001440000000004313773172540017006 0ustar liggesusers#define POSTGIS_SVN_REVISION 16016 lwgeom/src/Makevars.win0000644000176200001440000000547514426521126014641 0ustar liggesusersCXX_STD = CXX STATLIB = liblwgeom/liblwgeomstatic.a OBJECTS_LIBLWGEOM= \ liblwgeom/varint.o \ liblwgeom/lwout_twkb.o \ liblwgeom/lwpsurface.o \ liblwgeom/lwtriangle.o \ liblwgeom/lwmpoly.o \ liblwgeom/lookup3.o \ liblwgeom/lwin_wkt.o \ liblwgeom/gserialized1.o \ liblwgeom/lwgeom.o \ liblwgeom/gserialized2.o \ liblwgeom/lwstroke.o \ liblwgeom/lwtin.o \ liblwgeom/lwin_twkb.o \ liblwgeom/lwgeom_geos_cluster.o \ liblwgeom/lwmsurface.o \ liblwgeom/lwgeom_wrapx.o \ liblwgeom/lwiterator.o \ liblwgeom/lwgeom_geos_node.o \ liblwgeom/lwout_geojson.o \ liblwgeom/lwgeom_debug.o \ liblwgeom/lwgeom_median.o \ liblwgeom/lwmval.o \ liblwgeom/lwkmeans.o \ liblwgeom/lwgeom_geos.o \ liblwgeom/lwout_kml.o \ liblwgeom/lwutil.o \ liblwgeom/lwprint.o \ liblwgeom/lwrandom.o \ liblwgeom/lwmline.o \ liblwgeom/lwgeodetic_tree.o \ liblwgeom/lwline.o \ liblwgeom/bytebuffer.o \ liblwgeom/lwgeodetic.o \ liblwgeom/measures.o \ liblwgeom/lwgeom_api.o \ liblwgeom/lwmcurve.o \ liblwgeom/lwcollection.o \ liblwgeom/gbox.o \ liblwgeom/lwspheroid.o \ liblwgeom/lwout_svg.o \ liblwgeom/lwin_encoded_polyline.o \ liblwgeom/lwout_encoded_polyline.o \ liblwgeom/lwgeom_geos_split.o \ liblwgeom/effectivearea.o \ liblwgeom/lwboundingcircle.o \ liblwgeom/lwcurvepoly.o \ liblwgeom/lwlinearreferencing.o \ liblwgeom/lwunionfind.o \ liblwgeom/lwchaikins.o \ liblwgeom/lwalgorithm.o \ liblwgeom/lwhomogenize.o \ liblwgeom/lwgeom_geos_clean.o \ liblwgeom/measures3d.o \ liblwgeom/lwout_x3d.o \ liblwgeom/lwgeom_transform.o \ liblwgeom/lwin_wkt_lex.o \ liblwgeom/lwmpoint.o \ liblwgeom/stringbuffer.o \ liblwgeom/lwcompound.o \ liblwgeom/gserialized.o \ liblwgeom/lwout_wkt.o \ liblwgeom/lwin_wkb.o \ liblwgeom/ptarray.o \ liblwgeom/lwout_wkb.o \ liblwgeom/lwpoly.o \ liblwgeom/lwpoint.o \ liblwgeom/lwout_gml.o \ liblwgeom/lwgeom_topo.o \ liblwgeom/lwcircstring.o \ liblwgeom/lwin_wkt_parse.o VERSION = 3.2.1 RWINLIB = ../windows/gdal3-$(VERSION) TARGET = lib$(subst gcc,,$(COMPILED_BY))$(R_ARCH) PKG_CPPFLAGS = \ -I./liblwgeom \ -I$(RWINLIB)/include/geos-3.9.0 \ -I$(RWINLIB)/include/proj-7.2.1 \ -DUSE_PROJ_H PKG_LIBS = \ -L./liblwgeom -llwgeomstatic \ -L$(RWINLIB)/$(TARGET) \ -L$(RWINLIB)/lib$(R_ARCH)$(CRT) \ -lproj -lgeos_c -lgeos -ljson-c -lexpat -lxml2 -liconv -lsqlite3 \ -ltiff -ljpeg -lcurl -lssh2 -lz -lssl -lcrypto -lgdi32 -lws2_32 -lcrypt32 -lwldap32 all: clean winlibs $(SHLIB): $(STATLIB) $(STATLIB): $(OBJECTS_LIBLWGEOM) $(OBJECTS_LIBLWGEOM): winlibs clean: rm -f $(SHLIB) $(OBJECTS) $(STATLIB) $(OBJECTS_LIBLWGEOM) winlibs: cp postgis_config.win postgis_config.h mkdir -p ../inst "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" --vanilla "../tools/winlibs.R" $(VERSION) # cp -r $(RWINLIB)/share/proj ../inst/ .PHONY: all winlibs clean lwgeom/src/proj.cpp0000644000176200001440000000202013773172540014014 0ustar liggesusers#include #include "Rcpp.h" #ifdef USE_PROJ_H #include std::string CPL_proj_version(bool b = false) { std::stringstream buffer; buffer << PROJ_VERSION_MAJOR << "." << PROJ_VERSION_MINOR << "." << PROJ_VERSION_PATCH; return buffer.str(); } Rcpp::LogicalVector CPL_use_proj4_init_rules(Rcpp::IntegerVector v) { proj_context_use_proj4_init_rules(PJ_DEFAULT_CTX, v[0]); return true; } Rcpp::LogicalVector CPL_set_data_dir(std::string data_dir) { const char *cp = data_dir.c_str(); proj_context_set_search_paths(PJ_DEFAULT_CTX, 1, &cp); return true; } #else #include // [[Rcpp::export]] std::string CPL_proj_version(bool b = false) { int v = PJ_VERSION; std::stringstream buffer; buffer << v / 100 << "." << (v / 10) % 10 << "." << v % 10; return buffer.str(); } // [[Rcpp::export]] Rcpp::LogicalVector CPL_use_proj4_init_rules(Rcpp::IntegerVector v) { return false; } // [[Rcpp::export]] Rcpp::LogicalVector CPL_set_data_dir(std::string data_dir) { // #nocov start return false; } #endif lwgeom/src/io.cpp0000644000176200001440000000136513773172540013464 0ustar liggesusers#include extern "C" { #include } #define LW_MSG_MAXLEN 256 // #nocov start static void io_notice(const char *fmt, va_list ap) { char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; Rprintf("%s\n", msg); } static void io_debug(int level, const char *fmt, va_list ap) { } static void io_error(const char *fmt, va_list ap) { char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; Rprintf("%s\n", msg); Rcpp::stop("lwgeom error"); } // #nocov end // [[Rcpp::export]] Rcpp::List CPL_init_lwgeom(Rcpp::List l) { lwgeom_set_debuglogger(io_debug); lwgeom_set_handlers(NULL, NULL, NULL, io_error, io_notice); return l; } lwgeom/src/geodetic.cpp0000644000176200001440000000673613773172540014647 0ustar liggesusers#include #include extern "C" { #include } using namespace Rcpp; #include "lwgeom.h" // sfc <--> lwgeom // [[Rcpp::export]] Rcpp::NumericVector CPL_geodetic_area(Rcpp::List sfc, double semi_major, double inv_flattening) { Rcpp::NumericVector ret(sfc.size()); std::vector lw = lwgeom_from_sfc(sfc); SPHEROID s; spheroid_init(&s, semi_major, semi_major * (1.0 - 1.0/inv_flattening)); for (size_t i = 0; i < lw.size(); i++) { ret[i] = lwgeom_area_spheroid(lw[i], &s); lwgeom_free(lw[i]); } return ret; } // [[Rcpp::export]] Rcpp::NumericVector CPL_geodetic_length(Rcpp::List sfc, double semi_major, double inv_flattening) { Rcpp::NumericVector ret(sfc.size()); std::vector lw = lwgeom_from_sfc(sfc); SPHEROID s; spheroid_init(&s, semi_major, semi_major * (1.0 - 1.0/inv_flattening)); for (size_t i = 0; i < lw.size(); i++) { ret[i] = lwgeom_length_spheroid(lw[i], &s); lwgeom_free(lw[i]); } return ret; } // [[Rcpp::export]] Rcpp::NumericVector CPL_geodetic_azimuth(Rcpp::List sfc, double semi_major, double inv_flattening) { if (sfc.size() < 1) stop("bearing needs at least 2 points"); // #nocov Rcpp::NumericVector ret(sfc.size() - 1); std::vector lw = lwgeom_from_sfc(sfc); SPHEROID s; spheroid_init(&s, semi_major, semi_major * (1.0 - 1.0/inv_flattening)); for (int i = 0; i < ret.size(); i++) { ret[i] = lwgeom_azumith_spheroid((LWPOINT*) lw[i], (LWPOINT*) lw[i+1], &s); lwgeom_free(lw[i]); } lwgeom_free(lw[ret.size()]); // last return ret; } // [[Rcpp::export]] Rcpp::List CPL_geodetic_segmentize(Rcpp::List sfc, double max_seg_length) { std::vector lw = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lw.size(); i++) { LWGEOM *ret; ret = lwgeom_segmentize_sphere(lw[i], max_seg_length); lwgeom_free(lw[i]); lw[i] = ret; } return sfc_from_lwgeom(lw); } // [[Rcpp::export]] Rcpp::List CPL_geodetic_covers(Rcpp::List sfc1, Rcpp::List sfc2) { Rcpp::List ret(sfc1.size()); std::vector lw1 = lwgeom_from_sfc(sfc1); std::vector lw2 = lwgeom_from_sfc(sfc2); for (size_t i = 0; i < lw1.size(); i++) { std::vector idx; for (size_t j = 0; j < lw2.size(); j++) if (lwgeom_covers_lwgeom_sphere(lw1[i], lw2[j])) idx.push_back(j + 1); ret[i] = idx; } sfc_from_lwgeom(lw1); // free sfc_from_lwgeom(lw2); // free return ret; } // [[Rcpp::export]] Rcpp::List CPL_geodetic_distance(Rcpp::List sfc1, Rcpp::List sfc2, double semi_major, double inv_flattening, double tolerance, bool sparse, double semi_minor = -1.0) { Rcpp::List out(1); std::vector lw1 = lwgeom_from_sfc(sfc1); std::vector lw2 = lwgeom_from_sfc(sfc2); SPHEROID s; if (semi_minor > 0.0) spheroid_init(&s, semi_major, semi_minor); else spheroid_init(&s, semi_major, semi_major * (1.0 - 1.0/inv_flattening)); // #nocov FIXME if (sparse) { Rcpp::List lst(sfc1.size()); for (size_t i = 0; i < lw1.size(); i++) { Rcpp::IntegerVector iv; for (size_t j = 0; j < lw2.size(); j++) { if (lwgeom_distance_spheroid(lw1[i], lw2[j], &s, tolerance) <= tolerance) iv.push_back(j + 1); } lst(i) = iv; } out(0) = lst; } else { Rcpp::NumericMatrix mat(sfc1.size(), sfc2.size()); for (size_t i = 0; i < lw1.size(); i++) { for (size_t j = 0; j < lw2.size(); j++) { mat(i, j) = lwgeom_distance_spheroid(lw1[i], lw2[j], &s, tolerance); } Rcpp::checkUserInterrupt(); } out(0) = mat; } sfc_from_lwgeom(lw1); // free sfc_from_lwgeom(lw2); // free return out; } lwgeom/src/liblwgeom/0000755000176200001440000000000014432625425014323 5ustar liggesuserslwgeom/src/liblwgeom/lwpsurface.c0000644000176200001440000001130113773172540016640 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" LWPSURFACE* lwpsurface_add_lwpoly(LWPSURFACE *mobj, const LWPOLY *obj) { return (LWPSURFACE*)lwcollection_add_lwgeom((LWCOLLECTION*)mobj, (LWGEOM*)obj); } void lwpsurface_free(LWPSURFACE *psurf) { uint32_t i; if ( ! psurf ) return; if ( psurf->bbox ) lwfree(psurf->bbox); for ( i = 0; i < psurf->ngeoms; i++ ) if ( psurf->geoms && psurf->geoms[i] ) lwpoly_free(psurf->geoms[i]); if ( psurf->geoms ) lwfree(psurf->geoms); lwfree(psurf); } void printLWPSURFACE(LWPSURFACE *psurf) { uint32_t i, j; LWPOLY *patch; if (psurf->type != POLYHEDRALSURFACETYPE) lwerror("printLWPSURFACE called with something else than a POLYHEDRALSURFACE"); lwnotice("LWPSURFACE {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(psurf->flags)); lwnotice(" SRID = %i", (int)psurf->srid); lwnotice(" ngeoms = %i", (int)psurf->ngeoms); for (i=0; ingeoms; i++) { patch = (LWPOLY *) psurf->geoms[i]; for (j=0; jnrings; j++) { lwnotice(" RING # %i :",j); printPA(patch->rings[j]); } } lwnotice("}"); } /* * TODO rewrite all this stuff to be based on a truly topological model */ struct struct_psurface_arcs { double ax, ay, az; double bx, by, bz; uint32_t cnt, face; }; typedef struct struct_psurface_arcs *psurface_arcs; /* We supposed that the geometry is valid we could have wrong result if not */ int lwpsurface_is_closed(const LWPSURFACE *psurface) { uint32_t i, j, k; uint32_t narcs, carc; int found; psurface_arcs arcs; POINT4D pa, pb; LWPOLY *patch; /* If surface is not 3D, it's can't be closed */ if (!FLAGS_GET_Z(psurface->flags)) return 0; /* If surface is less than 4 faces hard to be closed too */ if (psurface->ngeoms < 4) return 0; /* Max theoretical arcs number if no one is shared ... */ for (i=0, narcs=0 ; i < psurface->ngeoms ; i++) { patch = (LWPOLY *) psurface->geoms[i]; narcs += patch->rings[0]->npoints - 1; } arcs = lwalloc(sizeof(struct struct_psurface_arcs) * narcs); for (i=0, carc=0; i < psurface->ngeoms ; i++) { patch = (LWPOLY *) psurface->geoms[i]; for (j=0; j < patch->rings[0]->npoints - 1; j++) { getPoint4d_p(patch->rings[0], j, &pa); getPoint4d_p(patch->rings[0], j+1, &pb); /* remove redundant points if any */ if (pa.x == pb.x && pa.y == pb.y && pa.z == pb.z) continue; /* Make sure to order the 'lower' point first */ if ( (pa.x > pb.x) || (pa.x == pb.x && pa.y > pb.y) || (pa.x == pb.x && pa.y == pb.y && pa.z > pb.z) ) { pa = pb; getPoint4d_p(patch->rings[0], j, &pb); } for (found=0, k=0; k < carc ; k++) { if ( ( arcs[k].ax == pa.x && arcs[k].ay == pa.y && arcs[k].az == pa.z && arcs[k].bx == pb.x && arcs[k].by == pb.y && arcs[k].bz == pb.z && arcs[k].face != i) ) { arcs[k].cnt++; found = 1; /* Look like an invalid PolyhedralSurface anyway not a closed one */ if (arcs[k].cnt > 2) { lwfree(arcs); return 0; } } } if (!found) { arcs[carc].cnt=1; arcs[carc].face=i; arcs[carc].ax = pa.x; arcs[carc].ay = pa.y; arcs[carc].az = pa.z; arcs[carc].bx = pb.x; arcs[carc].by = pb.y; arcs[carc].bz = pb.z; carc++; /* Look like an invalid PolyhedralSurface anyway not a closed one */ if (carc > narcs) { lwfree(arcs); return 0; } } } } /* A polyhedron is closed if each edge is shared by exactly 2 faces */ for (k=0; k < carc ; k++) { if (arcs[k].cnt != 2) { lwfree(arcs); return 0; } } lwfree(arcs); /* Invalid Polyhedral case */ if (carc < psurface->ngeoms) return 0; return 1; } lwgeom/src/liblwgeom/lwout_geojson.c0000644000176200001440000004755114343212777017404 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2003 Refractions Research Inc. * Copyright 2009-2010 Olivier Courtin * **********************************************************************/ #include "liblwgeom_internal.h" #include /* strlen */ #include static char *asgeojson_point(const LWPOINT *point, char *srs, GBOX *bbox, int precision); static char *asgeojson_line(const LWLINE *line, char *srs, GBOX *bbox, int precision); static char *asgeojson_triangle(const LWTRIANGLE *tri, char *srs, GBOX *bbox, int precision); static char *asgeojson_poly(const LWPOLY *poly, char *srs, GBOX *bbox, int precision); static char *asgeojson_multipoint(const LWMPOINT *mpoint, char *srs, GBOX *bbox, int precision); static char *asgeojson_multiline(const LWMLINE *mline, char *srs, GBOX *bbox, int precision); static char *asgeojson_multipolygon(const LWMPOLY *mpoly, char *srs, GBOX *bbox, int precision); static char *asgeojson_collection(const LWCOLLECTION *col, char *srs, GBOX *bbox, int precision); static size_t asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision); static size_t asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision); static size_t pointArray_to_geojson(POINTARRAY *pa, char *buf, int precision); static size_t pointArray_geojson_size(POINTARRAY *pa, int precision); /** * Takes a GEOMETRY and returns a GeoJson representation */ char * lwgeom_to_geojson(const LWGEOM *geom, char *srs, int precision, int has_bbox) { int type = geom->type; GBOX *bbox = NULL; GBOX tmp; if ( precision > OUT_MAX_DOUBLE_PRECISION ) precision = OUT_MAX_DOUBLE_PRECISION; if (has_bbox) { /* Whether these are geography or geometry, the GeoJSON expects a cartesian bounding box */ lwgeom_calculate_gbox_cartesian(geom, &tmp); bbox = &tmp; } switch (type) { case POINTTYPE: return asgeojson_point((LWPOINT*)geom, srs, bbox, precision); case LINETYPE: return asgeojson_line((LWLINE*)geom, srs, bbox, precision); case POLYGONTYPE: return asgeojson_poly((LWPOLY*)geom, srs, bbox, precision); case MULTIPOINTTYPE: return asgeojson_multipoint((LWMPOINT*)geom, srs, bbox, precision); case MULTILINETYPE: return asgeojson_multiline((LWMLINE*)geom, srs, bbox, precision); case MULTIPOLYGONTYPE: return asgeojson_multipolygon((LWMPOLY*)geom, srs, bbox, precision); case TRIANGLETYPE: return asgeojson_triangle((LWTRIANGLE *)geom, srs, bbox, precision); case TINTYPE: case COLLECTIONTYPE: return asgeojson_collection((LWCOLLECTION*)geom, srs, bbox, precision); default: lwerror("lwgeom_to_geojson: '%s' geometry type not supported", lwtype_name(type)); } /* Never get here */ return NULL; } /** * Handle SRS */ static size_t asgeojson_srs_size(char *srs) { int size; size = sizeof("'crs':{'type':'name',"); size += sizeof("'properties':{'name':''}},"); size += strlen(srs) * sizeof(char); return size; } static size_t asgeojson_srs_buf(char *output, char *srs) { char *ptr = output; ptr += snprintf(ptr, strlen(ptr), "\"crs\":{\"type\":\"name\","); ptr += snprintf(ptr, strlen(ptr), "\"properties\":{\"name\":\"%s\"}},", srs); return (ptr-output); } /** * Handle Bbox */ static size_t asgeojson_bbox_size(int hasz, int precision) { int size; if (!hasz) { size = sizeof("\"bbox\":[,,,],"); size += 2 * 2 * (OUT_MAX_DIGS_DOUBLE + precision); } else { size = sizeof("\"bbox\":[,,,,,],"); size += 2 * 3 * (OUT_MAX_DIGS_DOUBLE + precision); } return size; } static size_t asgeojson_bbox_buf(char *output, GBOX *bbox, int hasz, int precision) { char *ptr = output; if (!hasz) ptr += snprintf(ptr, strlen(ptr), "\"bbox\":[%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->xmax, precision, bbox->ymax); else ptr += snprintf(ptr, strlen(ptr), "\"bbox\":[%.*f,%.*f,%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->zmin, precision, bbox->xmax, precision, bbox->ymax, precision, bbox->zmax); return (ptr-output); } /** * Point Geometry */ static size_t asgeojson_point_size(const LWPOINT *point, char *srs, GBOX *bbox, int precision) { int size; size = pointArray_geojson_size(point->point, precision); size += sizeof("{'type':'Point',"); size += sizeof("'coordinates':}"); if ( lwpoint_is_empty(point) ) size += 2; /* [] */ if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(point->flags), precision); return size; } static size_t asgeojson_point_buf(const LWPOINT *point, char *srs, char *output, GBOX *bbox, int precision) { char *ptr = output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"Point\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(point->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":"); if ( lwpoint_is_empty(point) ) ptr += snprintf(ptr, strlen(ptr), "[]"); ptr += pointArray_to_geojson(point->point, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "}"); return (ptr-output); } static char * asgeojson_point(const LWPOINT *point, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_point_size(point, srs, bbox, precision); output = lwalloc(size); asgeojson_point_buf(point, srs, output, bbox, precision); return output; } /** * Triangle Geometry */ static size_t asgeojson_triangle_size(const LWTRIANGLE *tri, char *srs, GBOX *bbox, int precision) { int size; size = sizeof("{'type':'Polygon',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(tri->flags), precision); size += sizeof("'coordinates':[[]]}"); size += pointArray_geojson_size(tri->points, precision); return size; } static size_t asgeojson_triangle_buf(const LWTRIANGLE *tri, char *srs, char *output, GBOX *bbox, int precision) { char *ptr = output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"Polygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(tri->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":[["); ptr += pointArray_to_geojson(tri->points, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "]]}"); return (ptr - output); } static char * asgeojson_triangle(const LWTRIANGLE *tri, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_triangle_size(tri, srs, bbox, precision); output = lwalloc(size); asgeojson_triangle_buf(tri, srs, output, bbox, precision); return output; } /** * Line Geometry */ static size_t asgeojson_line_size(const LWLINE *line, char *srs, GBOX *bbox, int precision) { int size; size = sizeof("{'type':'LineString',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(line->flags), precision); size += sizeof("'coordinates':[]}"); size += pointArray_geojson_size(line->points, precision); return size; } static size_t asgeojson_line_buf(const LWLINE *line, char *srs, char *output, GBOX *bbox, int precision) { char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"LineString\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(line->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":["); ptr += pointArray_to_geojson(line->points, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr-output); } static char * asgeojson_line(const LWLINE *line, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_line_size(line, srs, bbox, precision); output = lwalloc(size); asgeojson_line_buf(line, srs, output, bbox, precision); return output; } /** * Polygon Geometry */ static size_t asgeojson_poly_size(const LWPOLY *poly, char *srs, GBOX *bbox, int precision) { size_t size; uint32_t i; size = sizeof("{\"type\":\"Polygon\","); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(poly->flags), precision); size += sizeof("\"coordinates\":["); for (i=0; inrings; i++) { size += pointArray_geojson_size(poly->rings[i], precision); size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_poly_buf(const LWPOLY *poly, char *srs, char *output, GBOX *bbox, int precision) { uint32_t i; char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"Polygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(poly->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":["); for (i=0; inrings; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "["); ptr += pointArray_to_geojson(poly->rings[i], ptr, precision); ptr += snprintf(ptr, strlen(ptr), "]"); } ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr-output); } static char * asgeojson_poly(const LWPOLY *poly, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_poly_size(poly, srs, bbox, precision); output = lwalloc(size); asgeojson_poly_buf(poly, srs, output, bbox, precision); return output; } /** * Multipoint Geometry */ static size_t asgeojson_multipoint_size(const LWMPOINT *mpoint, char *srs, GBOX *bbox, int precision) { LWPOINT * point; int size; uint32_t i; size = sizeof("{'type':'MultiPoint',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoint->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0; ingeoms; i++) { point = mpoint->geoms[i]; size += pointArray_geojson_size(point->point, precision); } size += sizeof(",") * i; return size; } static size_t asgeojson_multipoint_buf(const LWMPOINT *mpoint, char *srs, char *output, GBOX *bbox, int precision) { LWPOINT *point; uint32_t i; char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"MultiPoint\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoint->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); point = mpoint->geoms[i]; ptr += pointArray_to_geojson(point->point, ptr, precision); } ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr - output); } static char * asgeojson_multipoint(const LWMPOINT *mpoint, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_multipoint_size(mpoint, srs, bbox, precision); output = lwalloc(size); asgeojson_multipoint_buf(mpoint, srs, output, bbox, precision); return output; } /** * Multiline Geometry */ static size_t asgeojson_multiline_size(const LWMLINE *mline, char *srs, GBOX *bbox, int precision) { LWLINE * line; int size; uint32_t i; size = sizeof("{'type':'MultiLineString',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mline->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0 ; ingeoms; i++) { line = mline->geoms[i]; size += pointArray_geojson_size(line->points, precision); size += sizeof("[]"); } size += sizeof(",") * i; return size; } static size_t asgeojson_multiline_buf(const LWMLINE *mline, char *srs, char *output, GBOX *bbox, int precision) { LWLINE *line; uint32_t i; char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"MultiLineString\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mline->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "["); line = mline->geoms[i]; ptr += pointArray_to_geojson(line->points, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "]"); } ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr - output); } static char * asgeojson_multiline(const LWMLINE *mline, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_multiline_size(mline, srs, bbox, precision); output = lwalloc(size); asgeojson_multiline_buf(mline, srs, output, bbox, precision); return output; } /** * MultiPolygon Geometry */ static size_t asgeojson_multipolygon_size(const LWMPOLY *mpoly, char *srs, GBOX *bbox, int precision) { LWPOLY *poly; int size; uint32_t i, j; size = sizeof("{'type':'MultiPolygon',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoly->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0; i < mpoly->ngeoms; i++) { poly = mpoly->geoms[i]; for (j=0 ; j nrings ; j++) { size += pointArray_geojson_size(poly->rings[j], precision); size += sizeof("[]"); } size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_multipolygon_buf(const LWMPOLY *mpoly, char *srs, char *output, GBOX *bbox, int precision) { LWPOLY *poly; uint32_t i, j; char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"MultiPolygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoly->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "["); poly = mpoly->geoms[i]; for (j=0 ; j < poly->nrings ; j++) { if (j) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "["); ptr += pointArray_to_geojson(poly->rings[j], ptr, precision); ptr += snprintf(ptr, strlen(ptr), "]"); } ptr += snprintf(ptr, strlen(ptr), "]"); } ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr - output); } static char * asgeojson_multipolygon(const LWMPOLY *mpoly, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_multipolygon_size(mpoly, srs, bbox, precision); output = lwalloc(size); asgeojson_multipolygon_buf(mpoly, srs, output, bbox, precision); return output; } /** * Collection Geometry */ static size_t asgeojson_collection_size(const LWCOLLECTION *col, char *srs, GBOX *bbox, int precision) { uint32_t i; size_t size; LWGEOM *subgeom; size = sizeof("{'type':'GeometryCollection',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(col->flags), precision); size += sizeof("'geometries':"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += asgeojson_geom_size(subgeom, NULL, precision); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_collection_buf(const LWCOLLECTION *col, char *srs, char *output, GBOX *bbox, int precision) { uint32_t i; char *ptr=output; LWGEOM *subgeom; ptr += snprintf(ptr, strlen(ptr), "{\"type\":\"GeometryCollection\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (col->ngeoms && bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(col->flags), precision); ptr += snprintf(ptr, strlen(ptr), "\"geometries\":["); for (i=0; ingeoms; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); subgeom = col->geoms[i]; ptr += asgeojson_geom_buf(subgeom, ptr, NULL, precision); } ptr += snprintf(ptr, strlen(ptr), "]}"); return (ptr - output); } static char * asgeojson_collection(const LWCOLLECTION *col, char *srs, GBOX *bbox, int precision) { char *output; int size; size = asgeojson_collection_size(col, srs, bbox, precision); output = lwalloc(size); asgeojson_collection_buf(col, srs, output, bbox, precision); return output; } static size_t asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision) { switch (geom->type) { case POINTTYPE: return asgeojson_point_size((LWPOINT *)geom, NULL, bbox, precision); case LINETYPE: return asgeojson_line_size((LWLINE *)geom, NULL, bbox, precision); case TRIANGLETYPE: return asgeojson_triangle_size((LWTRIANGLE *)geom, NULL, bbox, precision); case POLYGONTYPE: return asgeojson_poly_size((LWPOLY *)geom, NULL, bbox, precision); case MULTIPOINTTYPE: return asgeojson_multipoint_size((LWMPOINT *)geom, NULL, bbox, precision); case MULTILINETYPE: return asgeojson_multiline_size((LWMLINE *)geom, NULL, bbox, precision); case MULTIPOLYGONTYPE: return asgeojson_multipolygon_size((LWMPOLY *)geom, NULL, bbox, precision); default: lwerror("GeoJson: geometry not supported."); return 0; } } static size_t asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision) { int type = geom->type; char *ptr=output; switch (type) { case POINTTYPE: ptr += asgeojson_point_buf((LWPOINT*)geom, NULL, ptr, bbox, precision); break; case LINETYPE: ptr += asgeojson_line_buf((LWLINE*)geom, NULL, ptr, bbox, precision); break; case POLYGONTYPE: ptr += asgeojson_poly_buf((LWPOLY*)geom, NULL, ptr, bbox, precision); break; case TRIANGLETYPE: ptr += asgeojson_triangle_buf((LWTRIANGLE *)geom, NULL, ptr, bbox, precision); break; case MULTIPOINTTYPE: ptr += asgeojson_multipoint_buf((LWMPOINT*)geom, NULL, ptr, bbox, precision); break; case MULTILINETYPE: ptr += asgeojson_multiline_buf((LWMLINE*)geom, NULL, ptr, bbox, precision); break; case MULTIPOLYGONTYPE: ptr += asgeojson_multipolygon_buf((LWMPOLY*)geom, NULL, ptr, bbox, precision); break; default: if (bbox) lwfree(bbox); lwerror("GeoJson: geometry not supported."); } return (ptr-output); } static size_t pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) { uint32_t i; char *ptr; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; char z[OUT_DOUBLE_BUFFER_SIZE]; assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); ptr = output; /* TODO: rewrite this loop to be simpler and possibly quicker */ if (!FLAGS_GET_Z(pa->flags)) { for (i=0; inpoints; i++) { const POINT2D *pt; pt = getPoint2d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "[%s,%s]", x, y); } } else { for (i=0; inpoints; i++) { const POINT3D *pt = getPoint3d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->z, precision, z, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), ","); ptr += snprintf(ptr, strlen(ptr), "[%s,%s,%s]", x, y, z); } } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_geojson_size(POINTARRAY *pa, int precision) { assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); if (FLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(",")) * 2 * pa->npoints + sizeof(",[]"); return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(",,")) * 3 * pa->npoints + sizeof(",[]"); } lwgeom/src/liblwgeom/lwgeodetic.c0000644000176200001440000026676013773172540016640 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * Copyright 2009 David Skea * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeodetic.h" #include "lwgeom_log.h" /** * For testing geodetic bounding box, we have a magic global variable. * When this is true (when the cunit tests set it), use the slow, but * guaranteed correct, algorithm. Otherwise use the regular one. */ int gbox_geocentric_slow = LW_FALSE; /** * Utility function for ptarray_contains_point_sphere() */ static int point3d_equals(const POINT3D *p1, const POINT3D *p2) { return FP_EQUALS(p1->x, p2->x) && FP_EQUALS(p1->y, p2->y) && FP_EQUALS(p1->z, p2->z); } /** * Convert a longitude to the range of -PI,PI */ double longitude_radians_normalize(double lon) { if ( lon == -1.0 * M_PI ) return M_PI; if ( lon == -2.0 * M_PI ) return 0.0; if ( lon > 2.0 * M_PI ) lon = remainder(lon, 2.0 * M_PI); if ( lon < -2.0 * M_PI ) lon = remainder(lon, -2.0 * M_PI); if ( lon > M_PI ) lon = -2.0 * M_PI + lon; if ( lon < -1.0 * M_PI ) lon = 2.0 * M_PI + lon; if ( lon == -2.0 * M_PI ) lon *= -1.0; return lon; } /** * Convert a latitude to the range of -PI/2,PI/2 */ double latitude_radians_normalize(double lat) { if ( lat > 2.0 * M_PI ) lat = remainder(lat, 2.0 * M_PI); if ( lat < -2.0 * M_PI ) lat = remainder(lat, -2.0 * M_PI); if ( lat > M_PI ) lat = M_PI - lat; if ( lat < -1.0 * M_PI ) lat = -1.0 * M_PI - lat; if ( lat > M_PI_2 ) lat = M_PI - lat; if ( lat < -1.0 * M_PI_2 ) lat = -1.0 * M_PI - lat; return lat; } /** * Convert a longitude to the range of -180,180 * @param lon longitude in degrees */ double longitude_degrees_normalize(double lon) { if ( lon > 360.0 ) lon = remainder(lon, 360.0); if ( lon < -360.0 ) lon = remainder(lon, -360.0); if ( lon > 180.0 ) lon = -360.0 + lon; if ( lon < -180.0 ) lon = 360 + lon; if ( lon == -180.0 ) return 180.0; if ( lon == -360.0 ) return 0.0; return lon; } /** * Convert a latitude to the range of -90,90 * @param lat latitude in degrees */ double latitude_degrees_normalize(double lat) { if ( lat > 360.0 ) lat = remainder(lat, 360.0); if ( lat < -360.0 ) lat = remainder(lat, -360.0); if ( lat > 180.0 ) lat = 180.0 - lat; if ( lat < -180.0 ) lat = -180.0 - lat; if ( lat > 90.0 ) lat = 180.0 - lat; if ( lat < -90.0 ) lat = -180.0 - lat; return lat; } /** * Shift a point around by a number of radians */ void point_shift(GEOGRAPHIC_POINT *p, double shift) { double lon = p->lon + shift; if ( lon > M_PI ) p->lon = -1.0 * M_PI + (lon - M_PI); else p->lon = lon; return; } int geographic_point_equals(const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2) { return FP_EQUALS(g1->lat, g2->lat) && FP_EQUALS(g1->lon, g2->lon); } /** * Initialize a geographic point * @param lon longitude in degrees * @param lat latitude in degrees */ void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g) { g->lat = latitude_radians_normalize(deg2rad(lat)); g->lon = longitude_radians_normalize(deg2rad(lon)); } /** Returns the angular height (latitudinal span) of the box in radians */ double gbox_angular_height(const GBOX* gbox) { double d[6]; int i; double zmin = FLT_MAX; double zmax = -1 * FLT_MAX; POINT3D pt; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Generate all 8 corner vectors of the box */ for ( i = 0; i < 8; i++ ) { pt.x = d[i / 4]; pt.y = d[2 + (i % 4) / 2]; pt.z = d[4 + (i % 2)]; normalize(&pt); if ( pt.z < zmin ) zmin = pt.z; if ( pt.z > zmax ) zmax = pt.z; } return asin(zmax) - asin(zmin); } /** Returns the angular width (longitudinal span) of the box in radians */ double gbox_angular_width(const GBOX* gbox) { double d[6]; int i, j; POINT3D pt[3]; double maxangle; double magnitude; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Start with the bottom corner */ pt[0].x = gbox->xmin; pt[0].y = gbox->ymin; magnitude = sqrt(pt[0].x*pt[0].x + pt[0].y*pt[0].y); pt[0].x /= magnitude; pt[0].y /= magnitude; /* Generate all 8 corner vectors of the box */ /* Find the vector furthest from our seed vector */ for ( j = 0; j < 2; j++ ) { maxangle = -1 * FLT_MAX; for ( i = 0; i < 4; i++ ) { double angle, dotprod; POINT3D pt_n; pt_n.x = d[i / 2]; pt_n.y = d[2 + (i % 2)]; magnitude = sqrt(pt_n.x*pt_n.x + pt_n.y*pt_n.y); pt_n.x /= magnitude; pt_n.y /= magnitude; pt_n.z = 0.0; dotprod = pt_n.x*pt[j].x + pt_n.y*pt[j].y; angle = acos(dotprod > 1.0 ? 1.0 : dotprod); if ( angle > maxangle ) { pt[j+1] = pt_n; maxangle = angle; } } } /* Return the distance between the two furthest vectors */ return maxangle; } /** Computes the average(ish) center of the box and returns success. */ int gbox_centroid(const GBOX* gbox, POINT2D* out) { double d[6]; GEOGRAPHIC_POINT g; POINT3D pt; int i; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Zero out our return vector */ pt.x = pt.y = pt.z = 0.0; for ( i = 0; i < 8; i++ ) { POINT3D pt_n; pt_n.x = d[i / 4]; pt_n.y = d[2 + ((i % 4) / 2)]; pt_n.z = d[4 + (i % 2)]; normalize(&pt_n); pt.x += pt_n.x; pt.y += pt_n.y; pt.z += pt_n.z; } pt.x /= 8.0; pt.y /= 8.0; pt.z /= 8.0; normalize(&pt); cart2geog(&pt, &g); out->x = longitude_degrees_normalize(rad2deg(g.lon)); out->y = latitude_degrees_normalize(rad2deg(g.lat)); return LW_SUCCESS; } /** * Check to see if this geocentric gbox is wrapped around a pole. * Only makes sense if this gbox originated from a polygon, as it's assuming * the box is generated from external edges and there's an "interior" which * contains the pole. * * This function is overdetermined, for very large polygons it might add an * unwarranted pole. STILL NEEDS WORK! */ static int gbox_check_poles(GBOX *gbox) { int rv = LW_FALSE; #if POSTGIS_DEBUG_LEVEL >= 4 char *gbox_str = gbox_to_string(gbox); LWDEBUG(4, "checking poles"); LWDEBUGF(4, "gbox %s", gbox_str); lwfree(gbox_str); #endif /* Z axis */ if (gbox->xmin < 0.0 && gbox->xmax > 0.0 && gbox->ymin < 0.0 && gbox->ymax > 0.0) { /* Extrema lean positive */ if ((gbox->zmin > 0.0) && (gbox->zmax > 0.0)) { LWDEBUG(4, "enclosed positive z axis"); gbox->zmax = 1.0; } /* Extrema lean negative */ else if ((gbox->zmin < 0.0) && (gbox->zmax < 0.0)) { LWDEBUG(4, "enclosed negative z axis"); gbox->zmin = -1.0; } /* Extrema both sides! */ else { LWDEBUG(4, "enclosed both z axes"); gbox->zmin = -1.0; gbox->zmax = 1.0; } rv = LW_TRUE; } /* Y axis */ if (gbox->xmin < 0.0 && gbox->xmax > 0.0 && gbox->zmin < 0.0 && gbox->zmax > 0.0) { if ((gbox->ymin > 0.0) && (gbox->ymax > 0.0)) { LWDEBUG(4, "enclosed positive y axis"); gbox->ymax = 1.0; } else if ((gbox->ymin < 0.0) && (gbox->ymax < 0.0)) { LWDEBUG(4, "enclosed negative y axis"); gbox->ymax = -1.0; } else { LWDEBUG(4, "enclosed both y axes"); gbox->ymax = 1.0; gbox->ymin = -1.0; } rv = LW_TRUE; } /* X axis */ if (gbox->ymin < 0.0 && gbox->ymax > 0.0 && gbox->zmin < 0.0 && gbox->zmax > 0.0) { if ((gbox->xmin > 0.0) && (gbox->xmax > 0.0)) { LWDEBUG(4, "enclosed positive x axis"); gbox->xmax = 1.0; } else if ((gbox->xmin < 0.0) && (gbox->xmax < 0.0)) { LWDEBUG(4, "enclosed negative x axis"); gbox->xmin = -1.0; } else { LWDEBUG(4, "enclosed both x axes"); gbox->xmax = 1.0; gbox->xmin = -1.0; } rv = LW_TRUE; } return rv; } /** * Convert spherical coordinates to cartesian coordinates on unit sphere */ void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p) { p->x = cos(g->lat) * cos(g->lon); p->y = cos(g->lat) * sin(g->lon); p->z = sin(g->lat); } /** * Convert cartesian coordinates on unit sphere to spherical coordinates */ void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g) { g->lon = atan2(p->y, p->x); g->lat = asin(p->z); } /** * Convert lon/lat coordinates to cartesian coordinates on unit sphere */ void ll2cart(const POINT2D *g, POINT3D *p) { double x_rad = M_PI * g->x / 180.0; double y_rad = M_PI * g->y / 180.0; double cos_y_rad = cos(y_rad); p->x = cos_y_rad * cos(x_rad); p->y = cos_y_rad * sin(x_rad); p->z = sin(y_rad); } /** * Convert cartesian coordinates on unit sphere to lon/lat coordinates static void cart2ll(const POINT3D *p, POINT2D *g) { g->x = longitude_degrees_normalize(180.0 * atan2(p->y, p->x) / M_PI); g->y = latitude_degrees_normalize(180.0 * asin(p->z) / M_PI); } */ /** * Calculate the dot product of two unit vectors * (-1 == opposite, 0 == orthogonal, 1 == identical) */ static double dot_product(const POINT3D *p1, const POINT3D *p2) { return (p1->x*p2->x) + (p1->y*p2->y) + (p1->z*p2->z); } /** * Calculate the cross product of two vectors */ static void cross_product(const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->y * b->z - a->z * b->y; n->y = a->z * b->x - a->x * b->z; n->z = a->x * b->y - a->y * b->x; return; } /** * Calculate the sum of two vectors */ void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->x + b->x; n->y = a->y + b->y; n->z = a->z + b->z; return; } /** * Calculate the difference of two vectors */ static void vector_difference(const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->x - b->x; n->y = a->y - b->y; n->z = a->z - b->z; return; } /** * Scale a vector out by a factor */ void vector_scale(POINT3D *n, double scale) { n->x *= scale; n->y *= scale; n->z *= scale; return; } /* * static inline double vector_magnitude(const POINT3D* v) * { * return sqrt(v->x*v->x + v->y*v->y + v->z*v->z); * } */ /** * Angle between two unit vectors */ double vector_angle(const POINT3D* v1, const POINT3D* v2) { POINT3D v3, normal; double angle, x, y; cross_product(v1, v2, &normal); normalize(&normal); cross_product(&normal, v1, &v3); x = dot_product(v1, v2); y = dot_product(v2, &v3); angle = atan2(y, x); return angle; } /** * Normalize to a unit vector. */ static void normalize2d(POINT2D *p) { double d = sqrt(p->x*p->x + p->y*p->y); if (FP_IS_ZERO(d)) { p->x = p->y = 0.0; return; } p->x = p->x / d; p->y = p->y / d; return; } /** * Calculates the unit normal to two vectors, trying to avoid * problems with over-narrow or over-wide cases. */ void unit_normal(const POINT3D *P1, const POINT3D *P2, POINT3D *normal) { double p_dot = dot_product(P1, P2); POINT3D P3; /* If edge is really large, calculate a narrower equivalent angle A1/A3. */ if ( p_dot < 0 ) { vector_sum(P1, P2, &P3); normalize(&P3); } /* If edge is narrow, calculate a wider equivalent angle A1/A3. */ else if ( p_dot > 0.95 ) { vector_difference(P2, P1, &P3); normalize(&P3); } /* Just keep the current angle in A1/A3. */ else { P3 = *P2; } /* Normals to the A-plane and B-plane */ cross_product(P1, &P3, normal); normalize(normal); } /** * Rotates v1 through an angle (in radians) within the plane defined by v1/v2, returns * the rotated vector in n. */ void vector_rotate(const POINT3D* v1, const POINT3D* v2, double angle, POINT3D* n) { POINT3D u; double cos_a = cos(angle); double sin_a = sin(angle); double uxuy, uyuz, uxuz; double ux2, uy2, uz2; double rxx, rxy, rxz, ryx, ryy, ryz, rzx, rzy, rzz; /* Need a unit vector normal to rotate around */ unit_normal(v1, v2, &u); uxuy = u.x * u.y; uxuz = u.x * u.z; uyuz = u.y * u.z; ux2 = u.x * u.x; uy2 = u.y * u.y; uz2 = u.z * u.z; rxx = cos_a + ux2 * (1 - cos_a); rxy = uxuy * (1 - cos_a) - u.z * sin_a; rxz = uxuz * (1 - cos_a) + u.y * sin_a; ryx = uxuy * (1 - cos_a) + u.z * sin_a; ryy = cos_a + uy2 * (1 - cos_a); ryz = uyuz * (1 - cos_a) - u.x * sin_a; rzx = uxuz * (1 - cos_a) - u.y * sin_a; rzy = uyuz * (1 - cos_a) + u.x * sin_a; rzz = cos_a + uz2 * (1 - cos_a); n->x = rxx * v1->x + rxy * v1->y + rxz * v1->z; n->y = ryx * v1->x + ryy * v1->y + ryz * v1->z; n->z = rzx * v1->x + rzy * v1->y + rzz * v1->z; normalize(n); } /** * Normalize to a unit vector. */ void normalize(POINT3D *p) { double d = sqrt(p->x*p->x + p->y*p->y + p->z*p->z); if (FP_IS_ZERO(d)) { p->x = p->y = p->z = 0.0; return; } p->x = p->x / d; p->y = p->y / d; p->z = p->z / d; return; } /** * Computes the cross product of two vectors using their lat, lng representations. * Good even for small distances between p and q. */ void robust_cross_product(const GEOGRAPHIC_POINT *p, const GEOGRAPHIC_POINT *q, POINT3D *a) { double lon_qpp = (q->lon + p->lon) / -2.0; double lon_qmp = (q->lon - p->lon) / 2.0; double sin_p_lat_minus_q_lat = sin(p->lat-q->lat); double sin_p_lat_plus_q_lat = sin(p->lat+q->lat); double sin_lon_qpp = sin(lon_qpp); double sin_lon_qmp = sin(lon_qmp); double cos_lon_qpp = cos(lon_qpp); double cos_lon_qmp = cos(lon_qmp); a->x = sin_p_lat_minus_q_lat * sin_lon_qpp * cos_lon_qmp - sin_p_lat_plus_q_lat * cos_lon_qpp * sin_lon_qmp; a->y = sin_p_lat_minus_q_lat * cos_lon_qpp * cos_lon_qmp + sin_p_lat_plus_q_lat * sin_lon_qpp * sin_lon_qmp; a->z = cos(p->lat) * cos(q->lat) * sin(q->lon-p->lon); } void x_to_z(POINT3D *p) { double tmp = p->z; p->z = p->x; p->x = tmp; } void y_to_z(POINT3D *p) { double tmp = p->z; p->z = p->y; p->y = tmp; } int crosses_dateline(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e) { double sign_s = SIGNUM(s->lon); double sign_e = SIGNUM(e->lon); double ss = fabs(s->lon); double ee = fabs(e->lon); if ( sign_s == sign_e ) { return LW_FALSE; } else { double dl = ss + ee; if ( dl < M_PI ) return LW_FALSE; else if ( FP_EQUALS(dl, M_PI) ) return LW_FALSE; else return LW_TRUE; } } /** * Returns -1 if the point is to the left of the plane formed * by the edge, 1 if the point is to the right, and 0 if the * point is on the plane. */ static int edge_point_side(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { POINT3D normal, pt; double w; /* Normal to the plane defined by e */ robust_cross_product(&(e->start), &(e->end), &normal); normalize(&normal); geog2cart(p, &pt); /* We expect the dot product of with normal with any vector in the plane to be zero */ w = dot_product(&normal, &pt); LWDEBUGF(4,"dot product %.9g",w); if ( FP_IS_ZERO(w) ) { LWDEBUG(4, "point is on plane (dot product is zero)"); return 0; } if ( w < 0 ) return -1; else return 1; } /** * Returns the angle in radians at point B of the triangle formed by A-B-C */ static double sphere_angle(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { POINT3D normal1, normal2; robust_cross_product(b, a, &normal1); robust_cross_product(b, c, &normal2); normalize(&normal1); normalize(&normal2); return sphere_distance_cartesian(&normal1, &normal2); } /** * Computes the spherical area of a triangle. If C is to the left of A/B, * the area is negative. If C is to the right of A/B, the area is positive. * * @param a The first triangle vertex. * @param b The second triangle vertex. * @param c The last triangle vertex. * @return the signed area in radians. */ static double sphere_signed_area(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { double angle_a, angle_b, angle_c; double area_radians = 0.0; int side; GEOGRAPHIC_EDGE e; angle_a = sphere_angle(b,a,c); angle_b = sphere_angle(a,b,c); angle_c = sphere_angle(b,c,a); area_radians = angle_a + angle_b + angle_c - M_PI; /* What's the direction of the B/C edge? */ e.start = *a; e.end = *b; side = edge_point_side(&e, c); /* Co-linear points implies no area */ if ( side == 0 ) return 0.0; /* Add the sign to the area */ return side * area_radians; } /** * Returns true if the point p is on the great circle plane. * Forms the scalar triple product of A,B,p and if the volume of the * resulting parallelepiped is near zero the point p is on the * great circle plane. */ int edge_point_on_plane(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { int side = edge_point_side(e, p); if ( side == 0 ) return LW_TRUE; return LW_FALSE; } /** * Returns true if the point p is inside the cone defined by the * two ends of the edge e. */ int edge_point_in_cone(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { POINT3D vcp, vs, ve, vp; double vs_dot_vcp, vp_dot_vcp; geog2cart(&(e->start), &vs); geog2cart(&(e->end), &ve); /* Antipodal case, everything is inside. */ if ( vs.x == -1.0 * ve.x && vs.y == -1.0 * ve.y && vs.z == -1.0 * ve.z ) return LW_TRUE; geog2cart(p, &vp); /* The normalized sum bisects the angle between start and end. */ vector_sum(&vs, &ve, &vcp); normalize(&vcp); /* The projection of start onto the center defines the minimum similarity */ vs_dot_vcp = dot_product(&vs, &vcp); LWDEBUGF(4,"vs_dot_vcp %.19g",vs_dot_vcp); /* The projection of candidate p onto the center */ vp_dot_vcp = dot_product(&vp, &vcp); LWDEBUGF(4,"vp_dot_vcp %.19g",vp_dot_vcp); /* If p is more similar than start then p is inside the cone */ LWDEBUGF(4,"fabs(vp_dot_vcp - vs_dot_vcp) %.39g",fabs(vp_dot_vcp - vs_dot_vcp)); /* ** We want to test that vp_dot_vcp is >= vs_dot_vcp but there are ** numerical stability issues for values that are very very nearly ** equal. Unfortunately there are also values of vp_dot_vcp that are legitimately ** very close to but still less than vs_dot_vcp which we also need to catch. ** The tolerance of 10-17 seems to do the trick on 32-bit and 64-bit architectures, ** for the test cases here. ** However, tuning the tolerance value feels like a dangerous hack. ** Fundamentally, the problem is that this test is so sensitive. */ /* 1.1102230246251565404236316680908203125e-16 */ if ( vp_dot_vcp > vs_dot_vcp || fabs(vp_dot_vcp - vs_dot_vcp) < 2e-16 ) { LWDEBUG(4, "point is in cone"); return LW_TRUE; } LWDEBUG(4, "point is not in cone"); return LW_FALSE; } /** * True if the longitude of p is within the range of the longitude of the ends of e */ int edge_contains_coplanar_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { GEOGRAPHIC_EDGE g; GEOGRAPHIC_POINT q; double slon = fabs((e->start).lon) + fabs((e->end).lon); double dlon = fabs(fabs((e->start).lon) - fabs((e->end).lon)); double slat = (e->start).lat + (e->end).lat; LWDEBUGF(4, "e.start == GPOINT(%.6g %.6g) ", (e->start).lat, (e->start).lon); LWDEBUGF(4, "e.end == GPOINT(%.6g %.6g) ", (e->end).lat, (e->end).lon); LWDEBUGF(4, "p == GPOINT(%.6g %.6g) ", p->lat, p->lon); /* Copy values into working registers */ g = *e; q = *p; /* Vertical plane, we need to do this calculation in latitude */ if ( FP_EQUALS( g.start.lon, g.end.lon ) ) { LWDEBUG(4, "vertical plane, we need to do this calculation in latitude"); /* Supposed to be co-planar... */ if ( ! FP_EQUALS( q.lon, g.start.lon ) ) return LW_FALSE; if ( ( g.start.lat <= q.lat && q.lat <= g.end.lat ) || ( g.end.lat <= q.lat && q.lat <= g.start.lat ) ) { return LW_TRUE; } else { return LW_FALSE; } } /* Over the pole, we need normalize latitude and do this calculation in latitude */ if ( FP_EQUALS( slon, M_PI ) && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) || FP_EQUALS(dlon, M_PI) ) ) { LWDEBUG(4, "over the pole..."); /* Antipodal, everything (or nothing?) is inside */ if ( FP_EQUALS( slat, 0.0 ) ) return LW_TRUE; /* Point *is* the north pole */ if ( slat > 0.0 && FP_EQUALS(q.lat, M_PI_2 ) ) return LW_TRUE; /* Point *is* the south pole */ if ( slat < 0.0 && FP_EQUALS(q.lat, -1.0 * M_PI_2) ) return LW_TRUE; LWDEBUG(4, "coplanar?..."); /* Supposed to be co-planar... */ if ( ! FP_EQUALS( q.lon, g.start.lon ) ) return LW_FALSE; LWDEBUG(4, "north or south?..."); /* Over north pole, test based on south pole */ if ( slat > 0.0 ) { LWDEBUG(4, "over the north pole..."); if ( q.lat > FP_MIN(g.start.lat, g.end.lat) ) return LW_TRUE; else return LW_FALSE; } else /* Over south pole, test based on north pole */ { LWDEBUG(4, "over the south pole..."); if ( q.lat < FP_MAX(g.start.lat, g.end.lat) ) return LW_TRUE; else return LW_FALSE; } } /* Dateline crossing, flip everything to the opposite hemisphere */ else if ( slon > M_PI && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) ) ) { LWDEBUG(4, "crosses dateline, flip longitudes..."); if ( g.start.lon > 0.0 ) g.start.lon -= M_PI; else g.start.lon += M_PI; if ( g.end.lon > 0.0 ) g.end.lon -= M_PI; else g.end.lon += M_PI; if ( q.lon > 0.0 ) q.lon -= M_PI; else q.lon += M_PI; } if ( ( g.start.lon <= q.lon && q.lon <= g.end.lon ) || ( g.end.lon <= q.lon && q.lon <= g.start.lon ) ) { LWDEBUG(4, "true, this edge contains point"); return LW_TRUE; } LWDEBUG(4, "false, this edge does not contain point"); return LW_FALSE; } /** * Given two points on a unit sphere, calculate their distance apart in radians. */ double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e) { double d_lon = e->lon - s->lon; double cos_d_lon = cos(d_lon); double cos_lat_e = cos(e->lat); double sin_lat_e = sin(e->lat); double cos_lat_s = cos(s->lat); double sin_lat_s = sin(s->lat); double a1 = POW2(cos_lat_e * sin(d_lon)); double a2 = POW2(cos_lat_s * sin_lat_e - sin_lat_s * cos_lat_e * cos_d_lon); double a = sqrt(a1 + a2); double b = sin_lat_s * sin_lat_e + cos_lat_s * cos_lat_e * cos_d_lon; return atan2(a, b); } /** * Given two unit vectors, calculate their distance apart in radians. */ double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e) { return acos(FP_MIN(1.0, dot_product(s, e))); } /** * Given two points on a unit sphere, calculate the direction from s to e. */ double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d) { double heading = 0.0; double f; /* Starting from the poles? Special case. */ if ( FP_IS_ZERO(cos(s->lat)) ) return (s->lat > 0.0) ? M_PI : 0.0; f = (sin(e->lat) - sin(s->lat) * cos(d)) / (sin(d) * cos(s->lat)); if ( FP_EQUALS(f, 1.0) ) heading = 0.0; else if ( FP_EQUALS(f, -1.0) ) heading = M_PI; else if ( fabs(f) > 1.0 ) { LWDEBUGF(4, "f = %g", f); heading = acos(f); } else heading = acos(f); if ( sin(e->lon - s->lon) < 0.0 ) heading = -1 * heading; return heading; } #if 0 /* unused */ /** * Computes the spherical excess of a spherical triangle defined by * the three vertices A, B, C. Computes on the unit sphere (i.e., divides * edge lengths by the radius, even if the radius is 1.0). The excess is * signed based on the sign of the delta longitude of A and B. * * @param a The first triangle vertex. * @param b The second triangle vertex. * @param c The last triangle vertex. * @return the signed spherical excess. */ static double sphere_excess(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { double a_dist = sphere_distance(b, c); double b_dist = sphere_distance(c, a); double c_dist = sphere_distance(a, b); double hca = sphere_direction(c, a, b_dist); double hcb = sphere_direction(c, b, a_dist); double sign = SIGNUM(hcb-hca); double ss = (a_dist + b_dist + c_dist) / 2.0; double E = tan(ss/2.0)*tan((ss-a_dist)/2.0)*tan((ss-b_dist)/2.0)*tan((ss-c_dist)/2.0); return 4.0 * atan(sqrt(fabs(E))) * sign; } #endif /** * Returns true if the point p is on the minor edge defined by the * end points of e. */ int edge_contains_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { if ( edge_point_in_cone(e, p) && edge_point_on_plane(e, p) ) /* if ( edge_contains_coplanar_point(e, p) && edge_point_on_plane(e, p) ) */ { LWDEBUG(4, "point is on edge"); return LW_TRUE; } LWDEBUG(4, "point is not on edge"); return LW_FALSE; } /** * Used in great circle to compute the pole of the great circle. */ double z_to_latitude(double z, int top) { double sign = SIGNUM(z); double tlat = acos(z); LWDEBUGF(4, "inputs: z(%.8g) sign(%.8g) tlat(%.8g)", z, sign, tlat); if (FP_IS_ZERO(z)) { if (top) return M_PI_2; else return -1.0 * M_PI_2; } if (fabs(tlat) > M_PI_2 ) { tlat = sign * (M_PI - fabs(tlat)); } else { tlat = sign * tlat; } LWDEBUGF(4, "output: tlat(%.8g)", tlat); return tlat; } /** * Computes the pole of the great circle disk which is the intersection of * the great circle with the line of maximum/minimum gradient that lies on * the great circle plane. */ int clairaut_cartesian(const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom) { POINT3D t1, t2; GEOGRAPHIC_POINT vN1, vN2; LWDEBUG(4,"entering function"); unit_normal(start, end, &t1); unit_normal(end, start, &t2); LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z); LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z); cart2geog(&t1, &vN1); cart2geog(&t2, &vN2); g_top->lat = z_to_latitude(t1.z,LW_TRUE); g_top->lon = vN2.lon; g_bottom->lat = z_to_latitude(t2.z,LW_FALSE); g_bottom->lon = vN1.lon; LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon); LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon); return LW_SUCCESS; } /** * Computes the pole of the great circle disk which is the intersection of * the great circle with the line of maximum/minimum gradient that lies on * the great circle plane. */ int clairaut_geographic(const GEOGRAPHIC_POINT *start, const GEOGRAPHIC_POINT *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom) { POINT3D t1, t2; GEOGRAPHIC_POINT vN1, vN2; LWDEBUG(4,"entering function"); robust_cross_product(start, end, &t1); normalize(&t1); robust_cross_product(end, start, &t2); normalize(&t2); LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z); LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z); cart2geog(&t1, &vN1); cart2geog(&t2, &vN2); g_top->lat = z_to_latitude(t1.z,LW_TRUE); g_top->lon = vN2.lon; g_bottom->lat = z_to_latitude(t2.z,LW_FALSE); g_bottom->lon = vN1.lon; LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon); LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon); return LW_SUCCESS; } /** * Returns true if an intersection can be calculated, and places it in *g. * Returns false otherwise. */ int edge_intersection(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g) { POINT3D ea, eb, v; LWDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", e1->start.lat, e1->start.lon, e1->end.lat, e1->end.lon); LWDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", e2->start.lat, e2->start.lon, e2->end.lat, e2->end.lon); LWDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e1->start.lon), rad2deg(e1->start.lat), rad2deg(e1->end.lon), rad2deg(e1->end.lat)); LWDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e2->start.lon), rad2deg(e2->start.lat), rad2deg(e2->end.lon), rad2deg(e2->end.lat)); if ( geographic_point_equals(&(e1->start), &(e2->start)) ) { *g = e1->start; return LW_TRUE; } if ( geographic_point_equals(&(e1->end), &(e2->end)) ) { *g = e1->end; return LW_TRUE; } if ( geographic_point_equals(&(e1->end), &(e2->start)) ) { *g = e1->end; return LW_TRUE; } if ( geographic_point_equals(&(e1->start), &(e2->end)) ) { *g = e1->start; return LW_TRUE; } robust_cross_product(&(e1->start), &(e1->end), &ea); normalize(&ea); robust_cross_product(&(e2->start), &(e2->end), &eb); normalize(&eb); LWDEBUGF(4, "e1 cross product == POINT(%.12g %.12g %.12g)", ea.x, ea.y, ea.z); LWDEBUGF(4, "e2 cross product == POINT(%.12g %.12g %.12g)", eb.x, eb.y, eb.z); LWDEBUGF(4, "fabs(dot_product(ea, eb)) == %.14g", fabs(dot_product(&ea, &eb))); if ( FP_EQUALS(fabs(dot_product(&ea, &eb)), 1.0) ) { LWDEBUGF(4, "parallel edges found! dot_product = %.12g", dot_product(&ea, &eb)); /* Parallel (maybe equal) edges! */ /* Hack alert, only returning ONE end of the edge right now, most do better later. */ /* Hack alert #2, returning a value of 2 to indicate a co-linear crossing event. */ if ( edge_contains_point(e1, &(e2->start)) ) { *g = e2->start; return 2; } if ( edge_contains_point(e1, &(e2->end)) ) { *g = e2->end; return 2; } if ( edge_contains_point(e2, &(e1->start)) ) { *g = e1->start; return 2; } if ( edge_contains_point(e2, &(e1->end)) ) { *g = e1->end; return 2; } } unit_normal(&ea, &eb, &v); LWDEBUGF(4, "v == POINT(%.12g %.12g %.12g)", v.x, v.y, v.z); g->lat = atan2(v.z, sqrt(v.x * v.x + v.y * v.y)); g->lon = atan2(v.y, v.x); LWDEBUGF(4, "g == GPOINT(%.12g %.12g)", g->lat, g->lon); LWDEBUGF(4, "g == POINT(%.12g %.12g)", rad2deg(g->lon), rad2deg(g->lat)); if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) ) { return LW_TRUE; } else { LWDEBUG(4, "flipping point to other side of sphere"); g->lat = -1.0 * g->lat; g->lon = g->lon + M_PI; if ( g->lon > M_PI ) { g->lon = -1.0 * (2.0 * M_PI - g->lon); } if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) ) { return LW_TRUE; } } return LW_FALSE; } double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest) { double d1 = 1000000000.0, d2, d3, d_nearest; POINT3D n, p, k; GEOGRAPHIC_POINT gk, g_nearest; /* Zero length edge, */ if ( geographic_point_equals(&(e->start), &(e->end)) ) { *closest = e->start; return sphere_distance(&(e->start), gp); } robust_cross_product(&(e->start), &(e->end), &n); normalize(&n); geog2cart(gp, &p); vector_scale(&n, dot_product(&p, &n)); vector_difference(&p, &n, &k); normalize(&k); cart2geog(&k, &gk); if ( edge_contains_point(e, &gk) ) { d1 = sphere_distance(gp, &gk); } d2 = sphere_distance(gp, &(e->start)); d3 = sphere_distance(gp, &(e->end)); d_nearest = d1; g_nearest = gk; if ( d2 < d_nearest ) { d_nearest = d2; g_nearest = e->start; } if ( d3 < d_nearest ) { d_nearest = d3; g_nearest = e->end; } if (closest) *closest = g_nearest; return d_nearest; } /** * Calculate the distance between two edges. * IMPORTANT: this test does not check for edge intersection!!! (distance == 0) * You have to check for intersection before calling this function. */ double edge_distance_to_edge(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2) { double d; GEOGRAPHIC_POINT gcp1s, gcp1e, gcp2s, gcp2e, c1, c2; double d1s = edge_distance_to_point(e1, &(e2->start), &gcp1s); double d1e = edge_distance_to_point(e1, &(e2->end), &gcp1e); double d2s = edge_distance_to_point(e2, &(e1->start), &gcp2s); double d2e = edge_distance_to_point(e2, &(e1->end), &gcp2e); d = d1s; c1 = gcp1s; c2 = e2->start; if ( d1e < d ) { d = d1e; c1 = gcp1e; c2 = e2->end; } if ( d2s < d ) { d = d2s; c1 = e1->start; c2 = gcp2s; } if ( d2e < d ) { d = d2e; c1 = e1->end; c2 = gcp2e; } if ( closest1 ) *closest1 = c1; if ( closest2 ) *closest2 = c2; return d; } /** * Given a starting location r, a distance and an azimuth * to the new point, compute the location of the projected point on the unit sphere. */ int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n) { double d = distance; double lat1 = r->lat; double lon1 = r->lon; double lat2, lon2; lat2 = asin(sin(lat1)*cos(d) + cos(lat1)*sin(d)*cos(azimuth)); /* If we're going straight up or straight down, we don't need to calculate the longitude */ /* TODO: this isn't quite true, what if we're going over the pole? */ if ( FP_EQUALS(azimuth, M_PI) || FP_EQUALS(azimuth, 0.0) ) { lon2 = r->lon; } else { lon2 = lon1 + atan2(sin(azimuth)*sin(d)*cos(lat1), cos(d)-sin(lat1)*sin(lat2)); } if ( isnan(lat2) || isnan(lon2) ) return LW_FAILURE; n->lat = lat2; n->lon = lon2; return LW_SUCCESS; } int edge_calculate_gbox_slow(const GEOGRAPHIC_EDGE *e, GBOX *gbox) { int steps = 1000000; int i; double dx, dy, dz; double distance = sphere_distance(&(e->start), &(e->end)); POINT3D pn, p, start, end; /* Edge is zero length, just return the naive box */ if ( FP_IS_ZERO(distance) ) { LWDEBUG(4, "edge is zero length. returning"); geog2cart(&(e->start), &start); geog2cart(&(e->end), &end); gbox_init_point3d(&start, gbox); gbox_merge_point3d(&end, gbox); return LW_SUCCESS; } /* Edge is antipodal (one point on each side of the globe), set the box to contain the whole world and return */ if ( FP_EQUALS(distance, M_PI) ) { LWDEBUG(4, "edge is antipodal. setting to maximum size box, and returning"); gbox->xmin = gbox->ymin = gbox->zmin = -1.0; gbox->xmax = gbox->ymax = gbox->zmax = 1.0; return LW_SUCCESS; } /* Walk along the chord between start and end incrementally, normalizing at each step. */ geog2cart(&(e->start), &start); geog2cart(&(e->end), &end); dx = (end.x - start.x)/steps; dy = (end.y - start.y)/steps; dz = (end.z - start.z)/steps; p = start; gbox->xmin = gbox->xmax = p.x; gbox->ymin = gbox->ymax = p.y; gbox->zmin = gbox->zmax = p.z; for ( i = 0; i < steps; i++ ) { p.x += dx; p.y += dy; p.z += dz; pn = p; normalize(&pn); gbox_merge_point3d(&pn, gbox); } return LW_SUCCESS; } /** * The magic function, given an edge in spherical coordinates, calculate a * 3D bounding box that fully contains it, taking into account the curvature * of the sphere on which it is inscribed. * * Any arc on the sphere defines a plane that bisects the sphere. In this plane, * the arc is a portion of a unit circle. * Projecting the end points of the axes (1,0,0), (-1,0,0) etc, into the plane * and normalizing yields potential extrema points. Those points on the * side of the plane-dividing line formed by the end points that is opposite * the origin of the plane are extrema and should be added to the bounding box. */ int edge_calculate_gbox(const POINT3D *A1, const POINT3D *A2, GBOX *gbox) { POINT2D R1, R2, RX, O; POINT3D AN, A3; POINT3D X[6]; int i, o_side; /* Initialize the box with the edge end points */ gbox_init_point3d(A1, gbox); gbox_merge_point3d(A2, gbox); /* Zero length edge, just return! */ if ( p3d_same(A1, A2) ) return LW_SUCCESS; /* Error out on antipodal edge */ if ( FP_EQUALS(A1->x, -1*A2->x) && FP_EQUALS(A1->y, -1*A2->y) && FP_EQUALS(A1->z, -1*A2->z) ) { lwerror("Antipodal (180 degrees long) edge detected!"); return LW_FAILURE; } /* Create A3, a vector in the plane of A1/A2, orthogonal to A1 */ unit_normal(A1, A2, &AN); unit_normal(&AN, A1, &A3); /* Project A1 and A2 into the 2-space formed by the plane A1/A3 */ R1.x = 1.0; R1.y = 0.0; R2.x = dot_product(A2, A1); R2.y = dot_product(A2, &A3); /* Initialize our 3-space axis points (x+, x-, y+, y-, z+, z-) */ memset(X, 0, sizeof(POINT3D) * 6); X[0].x = X[2].y = X[4].z = 1.0; X[1].x = X[3].y = X[5].z = -1.0; /* Initialize a 2-space origin point. */ O.x = O.y = 0.0; /* What side of the line joining R1/R2 is O? */ o_side = lw_segment_side(&R1, &R2, &O); /* Add any extrema! */ for ( i = 0; i < 6; i++ ) { /* Convert 3-space axis points to 2-space unit vectors */ RX.x = dot_product(&(X[i]), A1); RX.y = dot_product(&(X[i]), &A3); normalize2d(&RX); /* Any axis end on the side of R1/R2 opposite the origin */ /* is an extreme point in the arc, so we add the 3-space */ /* version of the point on R1/R2 to the gbox */ if ( lw_segment_side(&R1, &R2, &RX) != o_side ) { POINT3D Xn; Xn.x = RX.x * A1->x + RX.y * A3.x; Xn.y = RX.x * A1->y + RX.y * A3.y; Xn.z = RX.x * A1->z + RX.y * A3.z; gbox_merge_point3d(&Xn, gbox); } } return LW_SUCCESS; } /* * When we have a globe-covering gbox but we still want an outside * point, we do this Very Bad Hack, which is look at the first two points * in the ring and then nudge a point to the left of that arc. * There is an assumption of convexity built in there, as well as that * the shape doesn't have a sharp reversal in it. It's ugly, but * it fixes some common cases (large selection polygons) that users * are generating. At some point all of geodetic needs a clean-room * rewrite. * There is also an assumption of CCW exterior ring, which is how the * GeoJSON spec defined geographic ring orientation. */ static int lwpoly_pt_outside_hack(const LWPOLY *poly, POINT2D *pt_outside) { GEOGRAPHIC_POINT g1, g2, gSum; POINT4D p1, p2; POINT3D q1, q2, qMid, qCross, qSum; POINTARRAY *pa; if (lwgeom_is_empty((LWGEOM*)poly)) return LW_FAILURE; if (poly->nrings < 1) return LW_FAILURE; pa = poly->rings[0]; if (pa->npoints < 2) return LW_FAILURE; /* First two points of ring */ getPoint4d_p(pa, 0, &p1); getPoint4d_p(pa, 1, &p2); /* Convert to XYZ unit vectors */ geographic_point_init(p1.x, p1.y, &g1); geographic_point_init(p2.x, p2.y, &g2); geog2cart(&g1, &q1); geog2cart(&g2, &q2); /* Mid-point of first two points */ vector_sum(&q1, &q2, &qMid); normalize(&qMid); /* Cross product of first two points (perpendicular) */ cross_product(&q1, &q2, &qCross); normalize(&qCross); /* Invert it to put it outside, and scale down */ vector_scale(&qCross, -0.2); /* Project midpoint to the right */ vector_sum(&qMid, &qCross, &qSum); normalize(&qSum); /* Convert back to lon/lat */ cart2geog(&qSum, &gSum); pt_outside->x = rad2deg(gSum.lon); pt_outside->y = rad2deg(gSum.lat); return LW_SUCCESS; } int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside) { int rv; /* Make sure we have boxes */ if ( poly->bbox ) { rv = gbox_pt_outside(poly->bbox, pt_outside); } else { GBOX gbox; lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox); rv = gbox_pt_outside(&gbox, pt_outside); } if (rv == LW_FALSE) return lwpoly_pt_outside_hack(poly, pt_outside); return rv; } /** * Given a unit geocentric gbox, return a lon/lat (degrees) coordinate point point that is * guaranteed to be outside the box (and therefore anything it contains). */ int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside) { double grow = M_PI / 180.0 / 60.0; /* one arc-minute */ int i; GBOX ge; POINT3D corners[8]; POINT3D pt; GEOGRAPHIC_POINT g; while ( grow < M_PI ) { /* Assign our box and expand it slightly. */ ge = *gbox; if ( ge.xmin > -1 ) ge.xmin -= grow; if ( ge.ymin > -1 ) ge.ymin -= grow; if ( ge.zmin > -1 ) ge.zmin -= grow; if ( ge.xmax < 1 ) ge.xmax += grow; if ( ge.ymax < 1 ) ge.ymax += grow; if ( ge.zmax < 1 ) ge.zmax += grow; /* Build our eight corner points */ corners[0].x = ge.xmin; corners[0].y = ge.ymin; corners[0].z = ge.zmin; corners[1].x = ge.xmin; corners[1].y = ge.ymax; corners[1].z = ge.zmin; corners[2].x = ge.xmin; corners[2].y = ge.ymin; corners[2].z = ge.zmax; corners[3].x = ge.xmax; corners[3].y = ge.ymin; corners[3].z = ge.zmin; corners[4].x = ge.xmax; corners[4].y = ge.ymax; corners[4].z = ge.zmin; corners[5].x = ge.xmax; corners[5].y = ge.ymin; corners[5].z = ge.zmax; corners[6].x = ge.xmin; corners[6].y = ge.ymax; corners[6].z = ge.zmax; corners[7].x = ge.xmax; corners[7].y = ge.ymax; corners[7].z = ge.zmax; LWDEBUG(4, "trying to use a box corner point..."); for ( i = 0; i < 8; i++ ) { normalize(&(corners[i])); LWDEBUGF(4, "testing corner %d: POINT(%.8g %.8g %.8g)", i, corners[i].x, corners[i].y, corners[i].z); if ( ! gbox_contains_point3d(gbox, &(corners[i])) ) { LWDEBUGF(4, "corner %d is outside our gbox", i); pt = corners[i]; normalize(&pt); cart2geog(&pt, &g); pt_outside->x = rad2deg(g.lon); pt_outside->y = rad2deg(g.lat); LWDEBUGF(4, "returning POINT(%.8g %.8g) as outside point", pt_outside->x, pt_outside->y); return LW_SUCCESS; } } /* Try a wider growth to push the corners outside the original box. */ grow *= 2.0; } /* This should never happen! */ // lwerror("BOOM! Could not generate outside point!"); return LW_FAILURE; } static int ptarray_segmentize_sphere_edge_recursive ( const POINT3D *p1, const POINT3D *p2, /* 3-space points we are interpolating between */ const POINT4D *v1, const POINT4D *v2, /* real values and z/m values */ double d, double max_seg_length, /* current segment length and segment limit */ POINTARRAY *pa) /* write out results here */ { GEOGRAPHIC_POINT g; /* Reached the terminal leaf in recursion. Add */ /* the left-most point to the pointarray here */ /* We recurse down the left side first, so outputs should */ /* end up added to the array in order this way */ if (d <= max_seg_length) { POINT4D p; cart2geog(p1, &g); p.x = v1->x; p.y = v1->y; p.z = v1->z; p.m = v1->m; return ptarray_append_point(pa, &p, LW_FALSE); } /* Find the mid-point and recurse on the left and then the right */ else { /* Calculate mid-point */ POINT3D mid; mid.x = (p1->x + p2->x) / 2.0; mid.y = (p1->y + p2->y) / 2.0; mid.z = (p1->z + p2->z) / 2.0; normalize(&mid); /* Calculate z/m mid-values */ POINT4D midv; cart2geog(&mid, &g); midv.x = rad2deg(g.lon); midv.y = rad2deg(g.lat); midv.z = (v1->z + v2->z) / 2.0; midv.m = (v1->m + v2->m) / 2.0; /* Recurse on the left first */ ptarray_segmentize_sphere_edge_recursive(p1, &mid, v1, &midv, d/2.0, max_seg_length, pa); ptarray_segmentize_sphere_edge_recursive(&mid, p2, &midv, v2, d/2.0, max_seg_length, pa); return LW_SUCCESS; } } /** * Create a new point array with no segment longer than the input segment length (expressed in radians!) * @param pa_in - input point array pointer * @param max_seg_length - maximum output segment length in radians */ static POINTARRAY* ptarray_segmentize_sphere(const POINTARRAY *pa_in, double max_seg_length) { POINTARRAY *pa_out; int hasz = ptarray_has_z(pa_in); int hasm = ptarray_has_m(pa_in); POINT4D p1, p2; POINT3D q1, q2; GEOGRAPHIC_POINT g1, g2; uint32_t i; /* Just crap out on crazy input */ if ( ! pa_in ) lwerror("%s: null input pointarray", __func__); if ( max_seg_length <= 0.0 ) lwerror("%s: maximum segment length must be positive", __func__); /* Empty starting array */ pa_out = ptarray_construct_empty(hasz, hasm, pa_in->npoints); /* Simple loop per edge */ for (i = 1; i < pa_in->npoints; i++) { getPoint4d_p(pa_in, i-1, &p1); getPoint4d_p(pa_in, i, &p2); geographic_point_init(p1.x, p1.y, &g1); geographic_point_init(p2.x, p2.y, &g2); /* Skip duplicate points (except in case of 2-point lines!) */ if ((pa_in->npoints > 2) && p4d_same(&p1, &p2)) continue; /* How long is this edge? */ double d = sphere_distance(&g1, &g2); if (d > max_seg_length) { geog2cart(&g1, &q1); geog2cart(&g2, &q2); /* 3-d end points, XYZM end point, current edge size, min edge size */ ptarray_segmentize_sphere_edge_recursive(&q1, &q2, &p1, &p2, d, max_seg_length, pa_out); } /* If we don't segmentize, we need to add first point manually */ else { ptarray_append_point(pa_out, &p1, LW_TRUE); } } /* Always add the last point */ ptarray_append_point(pa_out, &p2, LW_TRUE); return pa_out; } /** * Create a new, densified geometry where no segment is longer than max_seg_length. * Input geometry is not altered, output geometry must be freed by caller. * @param lwg_in = input geometry * @param max_seg_length = maximum segment length in radians */ LWGEOM* lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length) { POINTARRAY *pa_out; LWLINE *lwline; LWPOLY *lwpoly_in, *lwpoly_out; LWCOLLECTION *lwcol_in, *lwcol_out; uint32_t i; /* Reflect NULL */ if ( ! lwg_in ) return NULL; /* Clone empty */ if ( lwgeom_is_empty(lwg_in) ) return lwgeom_clone(lwg_in); switch (lwg_in->type) { case MULTIPOINTTYPE: case POINTTYPE: return lwgeom_clone_deep(lwg_in); break; case LINETYPE: lwline = lwgeom_as_lwline(lwg_in); pa_out = ptarray_segmentize_sphere(lwline->points, max_seg_length); return lwline_as_lwgeom(lwline_construct(lwg_in->srid, NULL, pa_out)); break; case POLYGONTYPE: lwpoly_in = lwgeom_as_lwpoly(lwg_in); lwpoly_out = lwpoly_construct_empty(lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in)); for ( i = 0; i < lwpoly_in->nrings; i++ ) { pa_out = ptarray_segmentize_sphere(lwpoly_in->rings[i], max_seg_length); lwpoly_add_ring(lwpoly_out, pa_out); } return lwpoly_as_lwgeom(lwpoly_out); break; case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: lwcol_in = lwgeom_as_lwcollection(lwg_in); lwcol_out = lwcollection_construct_empty(lwg_in->type, lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in)); for ( i = 0; i < lwcol_in->ngeoms; i++ ) { lwcollection_add_lwgeom(lwcol_out, lwgeom_segmentize_sphere(lwcol_in->geoms[i], max_seg_length)); } return lwcollection_as_lwgeom(lwcol_out); break; default: lwerror("lwgeom_segmentize_sphere: unsupported input geometry type: %d - %s", lwg_in->type, lwtype_name(lwg_in->type)); break; } lwerror("lwgeom_segmentize_sphere got to the end of the function, should not happen"); return NULL; } /** * Returns the area of the ring (ring must be closed) in square radians (surface of * the sphere is 4*PI). */ double ptarray_area_sphere(const POINTARRAY *pa) { uint32_t i; const POINT2D *p; GEOGRAPHIC_POINT a, b, c; double area = 0.0; /* Return zero on nonsensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; p = getPoint2d_cp(pa, 0); geographic_point_init(p->x, p->y, &a); p = getPoint2d_cp(pa, 1); geographic_point_init(p->x, p->y, &b); for ( i = 2; i < pa->npoints-1; i++ ) { p = getPoint2d_cp(pa, i); geographic_point_init(p->x, p->y, &c); area += sphere_signed_area(&a, &b, &c); b = c; } return fabs(area); } static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection) { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g1, g2; GEOGRAPHIC_POINT nearest1, nearest2; POINT3D A1, A2, B1, B2; const POINT2D *p; double distance; uint32_t i, j; int use_sphere = (s->a == s->b ? 1 : 0); /* Make result really big, so that everything will be smaller than it */ distance = FLT_MAX; /* Empty point arrays? Return negative */ if ( pa1->npoints == 0 || pa2->npoints == 0 ) return -1.0; /* Handle point/point case here */ if ( pa1->npoints == 1 && pa2->npoints == 1 ) { p = getPoint2d_cp(pa1, 0); geographic_point_init(p->x, p->y, &g1); p = getPoint2d_cp(pa2, 0); geographic_point_init(p->x, p->y, &g2); /* Sphere special case, axes equal */ distance = s->radius * sphere_distance(&g1, &g2); if ( use_sphere ) return distance; /* Below tolerance, actual distance isn't of interest */ else if ( distance < 0.95 * tolerance ) return distance; /* Close or greater than tolerance, get the real answer to be sure */ else return spheroid_distance(&g1, &g2, s); } /* Handle point/line case here */ if ( pa1->npoints == 1 || pa2->npoints == 1 ) { /* Handle one/many case here */ uint32_t i; const POINTARRAY *pa_one; const POINTARRAY *pa_many; if ( pa1->npoints == 1 ) { pa_one = pa1; pa_many = pa2; } else { pa_one = pa2; pa_many = pa1; } /* Initialize our point */ p = getPoint2d_cp(pa_one, 0); geographic_point_init(p->x, p->y, &g1); /* Initialize start of line */ p = getPoint2d_cp(pa_many, 0); geographic_point_init(p->x, p->y, &(e1.start)); /* Iterate through the edges in our line */ for ( i = 1; i < pa_many->npoints; i++ ) { double d; p = getPoint2d_cp(pa_many, i); geographic_point_init(p->x, p->y, &(e1.end)); /* Get the spherical distance between point and edge */ d = s->radius * edge_distance_to_point(&e1, &g1, &g2); /* New shortest distance! Record this distance / location */ if ( d < distance ) { distance = d; nearest2 = g2; } /* We've gotten closer than the tolerance... */ if ( d < tolerance ) { /* Working on a sphere? The answer is correct, return */ if ( use_sphere ) { return d; } /* Far enough past the tolerance that the spheroid calculation won't change things */ else if ( d < tolerance * 0.95 ) { return d; } /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */ else { d = spheroid_distance(&g1, &nearest2, s); /* Yes, closer than tolerance, return! */ if ( d < tolerance ) return d; } } e1.start = e1.end; } /* On sphere, return answer */ if ( use_sphere ) return distance; /* On spheroid, calculate final answer based on closest approach */ else return spheroid_distance(&g1, &nearest2, s); } /* Initialize start of line 1 */ p = getPoint2d_cp(pa1, 0); geographic_point_init(p->x, p->y, &(e1.start)); geog2cart(&(e1.start), &A1); /* Handle line/line case */ for ( i = 1; i < pa1->npoints; i++ ) { p = getPoint2d_cp(pa1, i); geographic_point_init(p->x, p->y, &(e1.end)); geog2cart(&(e1.end), &A2); /* Initialize start of line 2 */ p = getPoint2d_cp(pa2, 0); geographic_point_init(p->x, p->y, &(e2.start)); geog2cart(&(e2.start), &B1); for ( j = 1; j < pa2->npoints; j++ ) { double d; p = getPoint2d_cp(pa2, j); geographic_point_init(p->x, p->y, &(e2.end)); geog2cart(&(e2.end), &B2); LWDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon); LWDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon); LWDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon); LWDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon); if ( check_intersection && edge_intersects(&A1, &A2, &B1, &B2) ) { LWDEBUG(4,"edge intersection! returning 0.0"); return 0.0; } d = s->radius * edge_distance_to_edge(&e1, &e2, &g1, &g2); LWDEBUGF(4,"got edge_distance_to_edge %.8g", d); if ( d < distance ) { distance = d; nearest1 = g1; nearest2 = g2; } if ( d < tolerance ) { if ( use_sphere ) { return d; } else { d = spheroid_distance(&nearest1, &nearest2, s); if ( d < tolerance ) return d; } } /* Copy end to start to allow a new end value in next iteration */ e2.start = e2.end; B1 = B2; } /* Copy end to start to allow a new end value in next iteration */ e1.start = e1.end; A1 = A2; LW_ON_INTERRUPT(return -1.0); } LWDEBUGF(4,"finished all loops, returning %.8g", distance); if ( use_sphere ) return distance; else return spheroid_distance(&nearest1, &nearest2, s); } /** * Calculate the area of an LWGEOM. Anything except POLYGON, MULTIPOLYGON * and GEOMETRYCOLLECTION return zero immediately. Multi's recurse, polygons * calculate external ring area and subtract internal ring area. A GBOX is * required to calculate an outside point. */ double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid) { int type; double radius2 = spheroid->radius * spheroid->radius; assert(lwgeom); /* No area in nothing */ if ( lwgeom_is_empty(lwgeom) ) return 0.0; /* Read the geometry type number */ type = lwgeom->type; /* Anything but polygons and collections returns zero */ if ( ! ( type == POLYGONTYPE || type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ) ) return 0.0; /* Actually calculate area */ if ( type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)lwgeom; uint32_t i; double area = 0.0; /* Just in case there's no rings */ if ( poly->nrings < 1 ) return 0.0; /* First, the area of the outer ring */ area += radius2 * ptarray_area_sphere(poly->rings[0]); /* Subtract areas of inner rings */ for ( i = 1; i < poly->nrings; i++ ) { area -= radius2 * ptarray_area_sphere(poly->rings[i]); } return area; } /* Recurse into sub-geometries to get area */ if ( type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ) { LWCOLLECTION *col = (LWCOLLECTION*)lwgeom; uint32_t i; double area = 0.0; for ( i = 0; i < col->ngeoms; i++ ) { area += lwgeom_area_sphere(col->geoms[i], spheroid); } return area; } /* Shouldn't get here. */ return 0.0; } /** * Calculate a projected point given a source point, a distance and a bearing. * @param r - location of first point. * @param spheroid - spheroid definition. * @param distance - distance, in units of the spheroid def'n. * @param azimuth - azimuth in radians. * @return s - location of projected point. * */ LWPOINT* lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth) { GEOGRAPHIC_POINT geo_source, geo_dest; POINT4D pt_dest; double x, y; POINTARRAY *pa; LWPOINT *lwp; /* Normalize distance to be positive*/ if ( distance < 0.0 ) { distance = -distance; azimuth += M_PI; } /* Normalize azimuth */ azimuth -= 2.0 * M_PI * floor(azimuth / (2.0 * M_PI)); /* Check the distance validity */ if ( distance > (M_PI * spheroid->radius) ) { lwerror("Distance must not be greater than %g", M_PI * spheroid->radius); return NULL; } /* Convert to ta geodetic point */ x = lwpoint_get_x(r); y = lwpoint_get_y(r); geographic_point_init(x, y, &geo_source); /* Try the projection */ if( spheroid_project(&geo_source, spheroid, distance, azimuth, &geo_dest) == LW_FAILURE ) { LWDEBUGF(3, "Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance); lwerror("Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance); return NULL; } /* Build the output LWPOINT */ pa = ptarray_construct(0, 0, 1); pt_dest.x = rad2deg(longitude_radians_normalize(geo_dest.lon)); pt_dest.y = rad2deg(latitude_radians_normalize(geo_dest.lat)); pt_dest.z = pt_dest.m = 0.0; ptarray_set_point4d(pa, 0, &pt_dest); lwp = lwpoint_construct(r->srid, NULL, pa); lwgeom_set_geodetic(lwpoint_as_lwgeom(lwp), LW_TRUE); return lwp; } /** * Calculate a bearing (azimuth) given a source and destination point. * @param r - location of first point. * @param s - location of second point. * @param spheroid - spheroid definition. * @return azimuth - azimuth in radians. * */ double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid) { GEOGRAPHIC_POINT g1, g2; double x1, y1, x2, y2; /* Convert r to a geodetic point */ x1 = lwpoint_get_x(r); y1 = lwpoint_get_y(r); geographic_point_init(x1, y1, &g1); /* Convert s to a geodetic point */ x2 = lwpoint_get_x(s); y2 = lwpoint_get_y(s); geographic_point_init(x2, y2, &g2); /* Same point, return NaN */ if ( FP_EQUALS(x1, x2) && FP_EQUALS(y1, y2) ) { return NAN; } /* Do the direction calculation */ return spheroid_direction(&g1, &g2, spheroid); } /** * Calculate the distance between two LWGEOMs, using the coordinates are * longitude and latitude. Return immediately when the calculated distance drops * below the tolerance (useful for dwithin calculations). * Return a negative distance for incalculable cases. */ double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance) { uint8_t type1, type2; int check_intersection = LW_FALSE; GBOX gbox1, gbox2; gbox_init(&gbox1); gbox_init(&gbox2); assert(lwgeom1); assert(lwgeom2); LWDEBUGF(4, "entered function, tolerance %.8g", tolerance); /* What's the distance to an empty geometry? We don't know. Return a negative number so the caller can catch this case. */ if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) ) { return -1.0; } type1 = lwgeom1->type; type2 = lwgeom2->type; /* Make sure we have boxes */ if ( lwgeom1->bbox ) gbox1 = *(lwgeom1->bbox); else lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1); /* Make sure we have boxes */ if ( lwgeom2->bbox ) gbox2 = *(lwgeom2->bbox); else lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2); /* If the boxes aren't disjoint, we have to check for edge intersections */ if ( gbox_overlaps(&gbox1, &gbox2) ) check_intersection = LW_TRUE; /* Point/line combinations can all be handled with simple point array iterations */ if ( ( type1 == POINTTYPE || type1 == LINETYPE ) && ( type2 == POINTTYPE || type2 == LINETYPE ) ) { POINTARRAY *pa1, *pa2; if ( type1 == POINTTYPE ) pa1 = ((LWPOINT*)lwgeom1)->point; else pa1 = ((LWLINE*)lwgeom1)->points; if ( type2 == POINTTYPE ) pa2 = ((LWPOINT*)lwgeom2)->point; else pa2 = ((LWLINE*)lwgeom2)->points; return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection); } /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */ if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) || ( type2 == POLYGONTYPE && type1 == POINTTYPE ) ) { const POINT2D *p; LWPOLY *lwpoly; LWPOINT *lwpt; double distance = FLT_MAX; uint32_t i; if ( type1 == POINTTYPE ) { lwpt = (LWPOINT*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; } else { lwpt = (LWPOINT*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; } p = getPoint2d_cp(lwpt->point, 0); /* Point in polygon implies zero distance */ if ( lwpoly_covers_point2d(lwpoly, p) ) { return 0.0; } /* Not inside, so what's the actual distance? */ for ( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection); if ( ring_distance < distance ) distance = ring_distance; if ( distance < tolerance ) return distance; } return distance; } /* Line/polygon case, if start point-in-poly, return zero, else return distance. */ if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) || ( type2 == POLYGONTYPE && type1 == LINETYPE ) ) { const POINT2D *p; LWPOLY *lwpoly; LWLINE *lwline; double distance = FLT_MAX; uint32_t i; if ( type1 == LINETYPE ) { lwline = (LWLINE*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; } else { lwline = (LWLINE*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; } p = getPoint2d_cp(lwline->points, 0); LWDEBUG(4, "checking if a point of line is in polygon"); /* Point in polygon implies zero distance */ if ( lwpoly_covers_point2d(lwpoly, p) ) return 0.0; LWDEBUG(4, "checking ring distances"); /* Not contained, so what's the actual distance? */ for ( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection); LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance); if ( ring_distance < distance ) distance = ring_distance; if ( distance < tolerance ) return distance; } LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance); return distance; } /* Polygon/polygon case, if start point-in-poly, return zero, else * return distance. */ if (type1 == POLYGONTYPE && type2 == POLYGONTYPE) { const POINT2D* p; LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1; LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2; double distance = FLT_MAX; uint32_t i, j; /* Point of 2 in polygon 1 implies zero distance */ p = getPoint2d_cp(lwpoly1->rings[0], 0); if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0; /* Point of 1 in polygon 2 implies zero distance */ p = getPoint2d_cp(lwpoly2->rings[0], 0); if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0; /* Not contained, so what's the actual distance? */ for (i = 0; i < lwpoly1->nrings; i++) { for (j = 0; j < lwpoly2->nrings; j++) { double ring_distance = ptarray_distance_spheroid( lwpoly1->rings[i], lwpoly2->rings[j], spheroid, tolerance, check_intersection); if (ring_distance < distance) distance = ring_distance; if (distance < tolerance) return distance; } } return distance; } /* Recurse into collections */ if ( lwtype_is_collection(type1) ) { uint32_t i; double distance = FLT_MAX; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1; for ( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid( col->geoms[i], lwgeom2, spheroid, tolerance); if ( geom_distance < distance ) distance = geom_distance; if ( distance < tolerance ) return distance; } return distance; } /* Recurse into collections */ if ( lwtype_is_collection(type2) ) { uint32_t i; double distance = FLT_MAX; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2; for ( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance); if ( geom_distance < distance ) distance = geom_distance; if ( distance < tolerance ) return distance; } return distance; } lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1)); return -1.0; } int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2) { int type1, type2; GBOX gbox1, gbox2; gbox1.flags = gbox2.flags = 0; assert(lwgeom1); assert(lwgeom2); type1 = lwgeom1->type; type2 = lwgeom2->type; /* dim(geom2) > dim(geom1) always returns false (because geom2 is bigger) */ if ( (type1 == POINTTYPE && type2 == LINETYPE) || (type1 == POINTTYPE && type2 == POLYGONTYPE) || (type1 == LINETYPE && type2 == POLYGONTYPE) ) { LWDEBUG(4, "dimension of geom2 is bigger than geom1"); return LW_FALSE; } /* Make sure we have boxes */ if ( lwgeom1->bbox ) gbox1 = *(lwgeom1->bbox); else lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1); /* Make sure we have boxes */ if ( lwgeom2->bbox ) gbox2 = *(lwgeom2->bbox); else lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2); /* Handle the polygon/point case */ if ( type1 == POLYGONTYPE && type2 == POINTTYPE ) { POINT2D pt_to_test; getPoint2d_p(((LWPOINT*)lwgeom2)->point, 0, &pt_to_test); return lwpoly_covers_point2d((LWPOLY*)lwgeom1, &pt_to_test); } else if ( type1 == POLYGONTYPE && type2 == LINETYPE) { return lwpoly_covers_lwline((LWPOLY*)lwgeom1, (LWLINE*)lwgeom2); } else if ( type1 == POLYGONTYPE && type2 == POLYGONTYPE) { return lwpoly_covers_lwpoly((LWPOLY*)lwgeom1, (LWPOLY*)lwgeom2); } else if ( type1 == LINETYPE && type2 == POINTTYPE) { return lwline_covers_lwpoint((LWLINE*)lwgeom1, (LWPOINT*)lwgeom2); } else if ( type1 == LINETYPE && type2 == LINETYPE) { return lwline_covers_lwline((LWLINE*)lwgeom1, (LWLINE*)lwgeom2); } else if ( type1 == POINTTYPE && type2 == POINTTYPE) { return lwpoint_same((LWPOINT*)lwgeom1, (LWPOINT*)lwgeom2); } /* If any of the first argument parts covers the second argument, it's true */ if ( lwtype_is_collection( type1 ) ) { uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1; for ( i = 0; i < col->ngeoms; i++ ) { if ( lwgeom_covers_lwgeom_sphere(col->geoms[i], lwgeom2) ) { return LW_TRUE; } } return LW_FALSE; } /* Only if all of the second arguments are covered by the first argument is the condition true */ if ( lwtype_is_collection( type2 ) ) { uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2; for ( i = 0; i < col->ngeoms; i++ ) { if ( ! lwgeom_covers_lwgeom_sphere(lwgeom1, col->geoms[i]) ) { return LW_FALSE; } } return LW_TRUE; } /* Don't get here */ lwerror("lwgeom_covers_lwgeom_sphere: reached end of function without resolution"); return LW_FALSE; } /** * Given a polygon (lon/lat decimal degrees) and point (lon/lat decimal degrees) and * a guaranteed outside point (lon/lat decimal degrees) (calculate with gbox_pt_outside()) * return LW_TRUE if point is inside or on edge of polygon. */ int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test) { uint32_t i; int in_hole_count = 0; POINT3D p; GEOGRAPHIC_POINT gpt_to_test; POINT2D pt_outside; GBOX gbox; #if POSTGIS_DEBUG_LEVEL >= 4 char *geom_ewkt; #endif gbox.flags = 0; /* Nulls and empties don't contain anything! */ if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) ) { LWDEBUG(4,"returning false, geometry is empty or null"); return LW_FALSE; } /* Make sure we have boxes */ if ( poly->bbox ) gbox = *(poly->bbox); else lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox); /* Point not in box? Done! */ geographic_point_init(pt_to_test->x, pt_to_test->y, &gpt_to_test); geog2cart(&gpt_to_test, &p); if ( ! gbox_contains_point3d(&gbox, &p) ) { LWDEBUG(4, "the point is not in the box!"); return LW_FALSE; } /* Calculate our outside point from the gbox */ lwpoly_pt_outside(poly, &pt_outside); LWDEBUGF(4, "pt_outside POINT(%.18g %.18g)", pt_outside.x, pt_outside.y); LWDEBUGF(4, "pt_to_test POINT(%.18g %.18g)", pt_to_test->x, pt_to_test->y); #if POSTGIS_DEBUG_LEVEL >= 4 geom_ewkt = lwgeom_to_ewkt((LWGEOM*)poly); LWDEBUGF(4, "polygon %s", geom_ewkt); lwfree(geom_ewkt); geom_ewkt = gbox_to_string(&gbox); LWDEBUGF(4, "gbox %s", geom_ewkt); lwfree(geom_ewkt); #endif /* Not in outer ring? We're done! */ if ( ! ptarray_contains_point_sphere(poly->rings[0], &pt_outside, pt_to_test) ) { LWDEBUG(4,"returning false, point is outside ring"); return LW_FALSE; } LWDEBUGF(4, "testing %d rings", poly->nrings); /* But maybe point is in a hole... */ for ( i = 1; i < poly->nrings; i++ ) { LWDEBUGF(4, "ring test loop %d", i); /* Count up hole containment. Odd => outside boundary. */ if ( ptarray_contains_point_sphere(poly->rings[i], &pt_outside, pt_to_test) ) in_hole_count++; } LWDEBUGF(4, "in_hole_count == %d", in_hole_count); if ( in_hole_count % 2 ) { LWDEBUG(4,"returning false, inner ring containment count is odd"); return LW_FALSE; } LWDEBUG(4,"returning true, inner ring containment count is even"); return LW_TRUE; } /** * Given a polygon1 check if all points of polygon2 are inside polygon1 and no * intersections of the polygon edges occur. * return LW_TRUE if polygon is inside or on edge of polygon. */ int lwpoly_covers_lwpoly(const LWPOLY *poly1, const LWPOLY *poly2) { uint32_t i; /* Nulls and empties don't contain anything! */ if ( ! poly1 || lwgeom_is_empty((LWGEOM*)poly1) ) { LWDEBUG(4,"returning false, geometry1 is empty or null"); return LW_FALSE; } /* Nulls and empties don't contain anything! */ if ( ! poly2 || lwgeom_is_empty((LWGEOM*)poly2) ) { LWDEBUG(4,"returning false, geometry2 is empty or null"); return LW_FALSE; } /* check if all vertices of poly2 are inside poly1 */ for (i = 0; i < poly2->nrings; i++) { /* every other ring is a hole, check if point is inside the actual polygon */ if ( i % 2 == 0) { if (LW_FALSE == lwpoly_covers_pointarray(poly1, poly2->rings[i])) { LWDEBUG(4,"returning false, geometry2 has point outside of geometry1"); return LW_FALSE; } } else { if (LW_TRUE == lwpoly_covers_pointarray(poly1, poly2->rings[i])) { LWDEBUG(4,"returning false, geometry2 has point inside a hole of geometry1"); return LW_FALSE; } } } /* check for any edge intersections, so nothing is partially outside of poly1 */ for (i = 0; i < poly2->nrings; i++) { if (LW_TRUE == lwpoly_intersects_line(poly1, poly2->rings[i])) { LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1"); return LW_FALSE; } } /* no abort condition found, so the poly2 should be completly inside poly1 */ return LW_TRUE; } /** * */ int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line) { /* Nulls and empties don't contain anything! */ if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) ) { LWDEBUG(4,"returning false, geometry1 is empty or null"); return LW_FALSE; } /* Nulls and empties don't contain anything! */ if ( ! line || lwgeom_is_empty((LWGEOM*)line) ) { LWDEBUG(4,"returning false, geometry2 is empty or null"); return LW_FALSE; } if (LW_FALSE == lwpoly_covers_pointarray(poly, line->points)) { LWDEBUG(4,"returning false, geometry2 has point outside of geometry1"); return LW_FALSE; } /* check for any edge intersections, so nothing is partially outside of poly1 */ if (LW_TRUE == lwpoly_intersects_line(poly, line->points)) { LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1"); return LW_FALSE; } /* no abort condition found, so the poly2 should be completely inside poly1 */ return LW_TRUE; } /** * return LW_TRUE if all points are inside the polygon */ int lwpoly_covers_pointarray(const LWPOLY* lwpoly, const POINTARRAY* pta) { uint32_t i; for (i = 0; i < pta->npoints; i++) { const POINT2D* pt_to_test = getPoint2d_cp(pta, i); if ( LW_FALSE == lwpoly_covers_point2d(lwpoly, pt_to_test) ) { LWDEBUG(4,"returning false, geometry2 has point outside of geometry1"); return LW_FALSE; } } return LW_TRUE; } /** * Checks if any edges of lwpoly intersect with the line formed by the pointarray * return LW_TRUE if any intersection between the given polygon and the line */ int lwpoly_intersects_line(const LWPOLY* lwpoly, const POINTARRAY* line) { uint32_t i, j, k; POINT3D pa1, pa2, pb1, pb2; for (i = 0; i < lwpoly->nrings; i++) { for (j = 0; j < lwpoly->rings[i]->npoints - 1; j++) { const POINT2D* a1 = getPoint2d_cp(lwpoly->rings[i], j); const POINT2D* a2 = getPoint2d_cp(lwpoly->rings[i], j+1); /* Set up our stab line */ ll2cart(a1, &pa1); ll2cart(a2, &pa2); for (k = 0; k < line->npoints - 1; k++) { const POINT2D* b1 = getPoint2d_cp(line, k); const POINT2D* b2 = getPoint2d_cp(line, k+1); /* Set up our stab line */ ll2cart(b1, &pb1); ll2cart(b2, &pb2); int inter = edge_intersects(&pa1, &pa2, &pb1, &pb2); /* ignore same edges */ if (inter & PIR_INTERSECTS && !(inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR) ) { return LW_TRUE; } } } } return LW_FALSE; } /** * return LW_TRUE if any of the line segments covers the point */ int lwline_covers_lwpoint(const LWLINE* lwline, const LWPOINT* lwpoint) { uint32_t i; GEOGRAPHIC_POINT p; GEOGRAPHIC_EDGE e; for ( i = 0; i < lwline->points->npoints - 1; i++) { const POINT2D* a1 = getPoint2d_cp(lwline->points, i); const POINT2D* a2 = getPoint2d_cp(lwline->points, i+1); geographic_point_init(a1->x, a1->y, &(e.start)); geographic_point_init(a2->x, a2->y, &(e.end)); geographic_point_init(lwpoint_get_x(lwpoint), lwpoint_get_y(lwpoint), &p); if ( edge_contains_point(&e, &p) ) { return LW_TRUE; } } return LW_FALSE; } /** * Check if first and last point of line2 are covered by line1 and then each * point in between has to be one line1 in the exact same order * return LW_TRUE if all edge points of line2 are on line1 */ int lwline_covers_lwline(const LWLINE* lwline1, const LWLINE* lwline2) { uint32_t i, j; GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT p1, p2; int start = LW_FALSE; int changed = LW_FALSE; /* first point on line */ if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, 0))) { LWDEBUG(4,"returning false, first point of line2 is not covered by line1"); return LW_FALSE; } /* last point on line */ if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, lwline2->points->npoints - 1))) { LWDEBUG(4,"returning false, last point of line2 is not covered by line1"); return LW_FALSE; } j = 0; i = 0; while (i < lwline1->points->npoints - 1 && j < lwline2->points->npoints - 1) { changed = LW_FALSE; const POINT2D* a1 = getPoint2d_cp(lwline1->points, i); const POINT2D* a2 = getPoint2d_cp(lwline1->points, i+1); const POINT2D* b1 = getPoint2d_cp(lwline2->points, j); const POINT2D* b2 = getPoint2d_cp(lwline2->points, j+1); geographic_point_init(a1->x, a1->y, &(e1.start)); geographic_point_init(a2->x, a2->y, &(e1.end)); geographic_point_init(b1->x, b1->y, &p2); /* we already know, that the last point is on line1, so we're done */ if ( j == lwline2->points->npoints - 1) { return LW_TRUE; } else if (start == LW_TRUE) { /* point is on current line1 edge, check next point in line2 */ if ( edge_contains_point(&e1, &p2)) { j++; changed = LW_TRUE; } geographic_point_init(a1->x, a1->y, &(e2.start)); geographic_point_init(a2->x, b2->y, &(e2.end)); geographic_point_init(a1->x, a1->y, &p1); /* point is on current line2 edge, check next point in line1 */ if ( edge_contains_point(&e2, &p1)) { i++; changed = LW_TRUE; } /* no edge progressed -> point left one line */ if ( changed == LW_FALSE ) { LWDEBUG(4,"returning false, found point not covered by both lines"); return LW_FALSE; } else { continue; } } /* find first edge to cover line2 */ if (edge_contains_point(&e1, &p2)) { start = LW_TRUE; } /* next line1 edge */ i++; } /* no uncovered point found */ return LW_TRUE; } /** * This function can only be used on LWGEOM that is built on top of * GSERIALIZED, otherwise alignment errors will ensue. */ int getPoint2d_p_ro(const POINTARRAY *pa, uint32_t n, POINT2D **point) { uint8_t *pa_ptr = NULL; assert(pa); assert(n < pa->npoints); pa_ptr = getPoint_internal(pa, n); /* printf( "pa_ptr[0]: %g\n", *((double*)pa_ptr)); */ *point = (POINT2D*)pa_ptr; return LW_SUCCESS; } int ptarray_calculate_gbox_geodetic(const POINTARRAY *pa, GBOX *gbox) { uint32_t i; int first = LW_TRUE; const POINT2D *p; POINT3D A1, A2; GBOX edge_gbox; assert(gbox); assert(pa); gbox_init(&edge_gbox); edge_gbox.flags = gbox->flags; if ( pa->npoints == 0 ) return LW_FAILURE; if ( pa->npoints == 1 ) { p = getPoint2d_cp(pa, 0); ll2cart(p, &A1); gbox->xmin = gbox->xmax = A1.x; gbox->ymin = gbox->ymax = A1.y; gbox->zmin = gbox->zmax = A1.z; return LW_SUCCESS; } p = getPoint2d_cp(pa, 0); ll2cart(p, &A1); for ( i = 1; i < pa->npoints; i++ ) { p = getPoint2d_cp(pa, i); ll2cart(p, &A2); edge_calculate_gbox(&A1, &A2, &edge_gbox); /* Initialize the box */ if ( first ) { gbox_duplicate(&edge_gbox, gbox); first = LW_FALSE; } /* Expand the box where necessary */ else { gbox_merge(&edge_gbox, gbox); } A1 = A2; } return LW_SUCCESS; } static int lwpoint_calculate_gbox_geodetic(const LWPOINT *point, GBOX *gbox) { assert(point); return ptarray_calculate_gbox_geodetic(point->point, gbox); } static int lwline_calculate_gbox_geodetic(const LWLINE *line, GBOX *gbox) { assert(line); return ptarray_calculate_gbox_geodetic(line->points, gbox); } static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox) { GBOX ringbox; uint32_t i; int first = LW_TRUE; assert(poly); if ( poly->nrings == 0 ) return LW_FAILURE; ringbox.flags = gbox->flags; for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_calculate_gbox_geodetic(poly->rings[i], &ringbox) == LW_FAILURE ) return LW_FAILURE; if ( first ) { gbox_duplicate(&ringbox, gbox); first = LW_FALSE; } else { gbox_merge(&ringbox, gbox); } } /* If the box wraps a poly, push that axis to the absolute min/max as appropriate */ gbox_check_poles(gbox); return LW_SUCCESS; } static int lwtriangle_calculate_gbox_geodetic(const LWTRIANGLE *triangle, GBOX *gbox) { assert(triangle); return ptarray_calculate_gbox_geodetic(triangle->points, gbox); } static int lwcollection_calculate_gbox_geodetic(const LWCOLLECTION *coll, GBOX *gbox) { GBOX subbox; uint32_t i; int result = LW_FAILURE; int first = LW_TRUE; assert(coll); if ( coll->ngeoms == 0 ) return LW_FAILURE; subbox.flags = gbox->flags; for ( i = 0; i < coll->ngeoms; i++ ) { if ( lwgeom_calculate_gbox_geodetic((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS ) { /* Keep a copy of the sub-bounding box for later */ if ( coll->geoms[i]->bbox ) lwfree(coll->geoms[i]->bbox); coll->geoms[i]->bbox = gbox_copy(&subbox); if ( first ) { gbox_duplicate(&subbox, gbox); first = LW_FALSE; } else { gbox_merge(&subbox, gbox); } result = LW_SUCCESS; } } return result; } int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox) { int result = LW_FAILURE; LWDEBUGF(4, "got type %d", geom->type); /* Add a geodetic flag to the incoming gbox */ gbox->flags = lwflags(FLAGS_GET_Z(geom->flags),FLAGS_GET_M(geom->flags),1); switch (geom->type) { case POINTTYPE: result = lwpoint_calculate_gbox_geodetic((LWPOINT*)geom, gbox); break; case LINETYPE: result = lwline_calculate_gbox_geodetic((LWLINE *)geom, gbox); break; case POLYGONTYPE: result = lwpolygon_calculate_gbox_geodetic((LWPOLY *)geom, gbox); break; case TRIANGLETYPE: result = lwtriangle_calculate_gbox_geodetic((LWTRIANGLE *)geom, gbox); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: result = lwcollection_calculate_gbox_geodetic((LWCOLLECTION *)geom, gbox); break; default: lwerror("lwgeom_calculate_gbox_geodetic: unsupported input geometry type: %d - %s", geom->type, lwtype_name(geom->type)); break; } return result; } static int ptarray_check_geodetic(const POINTARRAY *pa) { uint32_t t; POINT2D pt; assert(pa); for (t=0; tnpoints; t++) { getPoint2d_p(pa, t, &pt); /* printf( "%d (%g, %g)\n", t, pt.x, pt.y); */ if ( pt.x < -180.0 || pt.y < -90.0 || pt.x > 180.0 || pt.y > 90.0 ) return LW_FALSE; } return LW_TRUE; } static int lwpoint_check_geodetic(const LWPOINT *point) { assert(point); return ptarray_check_geodetic(point->point); } static int lwline_check_geodetic(const LWLINE *line) { assert(line); return ptarray_check_geodetic(line->points); } static int lwpoly_check_geodetic(const LWPOLY *poly) { uint32_t i = 0; assert(poly); for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_check_geodetic(poly->rings[i]) == LW_FALSE ) return LW_FALSE; } return LW_TRUE; } static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle) { assert(triangle); return ptarray_check_geodetic(triangle->points); } static int lwcollection_check_geodetic(const LWCOLLECTION *col) { uint32_t i = 0; assert(col); for ( i = 0; i < col->ngeoms; i++ ) { if ( lwgeom_check_geodetic(col->geoms[i]) == LW_FALSE ) return LW_FALSE; } return LW_TRUE; } int lwgeom_check_geodetic(const LWGEOM *geom) { if ( lwgeom_is_empty(geom) ) return LW_TRUE; switch (geom->type) { case POINTTYPE: return lwpoint_check_geodetic((LWPOINT *)geom); case LINETYPE: return lwline_check_geodetic((LWLINE *)geom); case POLYGONTYPE: return lwpoly_check_geodetic((LWPOLY *)geom); case TRIANGLETYPE: return lwtriangle_check_geodetic((LWTRIANGLE *)geom); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_check_geodetic((LWCOLLECTION *)geom); default: lwerror("lwgeom_check_geodetic: unsupported input geometry type: %d - %s", geom->type, lwtype_name(geom->type)); } return LW_FALSE; } static int ptarray_force_geodetic(POINTARRAY *pa) { uint32_t t; int changed = LW_FALSE; POINT4D pt; assert(pa); for ( t=0; t < pa->npoints; t++ ) { getPoint4d_p(pa, t, &pt); if ( pt.x < -180.0 || pt.x > 180.0 || pt.y < -90.0 || pt.y > 90.0 ) { pt.x = longitude_degrees_normalize(pt.x); pt.y = latitude_degrees_normalize(pt.y); ptarray_set_point4d(pa, t, &pt); changed = LW_TRUE; } } return changed; } static int lwpoint_force_geodetic(LWPOINT *point) { assert(point); return ptarray_force_geodetic(point->point); } static int lwline_force_geodetic(LWLINE *line) { assert(line); return ptarray_force_geodetic(line->points); } static int lwpoly_force_geodetic(LWPOLY *poly) { uint32_t i = 0; int changed = LW_FALSE; assert(poly); for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_force_geodetic(poly->rings[i]) == LW_TRUE ) changed = LW_TRUE; } return changed; } static int lwcollection_force_geodetic(LWCOLLECTION *col) { uint32_t i = 0; int changed = LW_FALSE; assert(col); for ( i = 0; i < col->ngeoms; i++ ) { if ( lwgeom_force_geodetic(col->geoms[i]) == LW_TRUE ) changed = LW_TRUE; } return changed; } int lwgeom_force_geodetic(LWGEOM *geom) { switch ( lwgeom_get_type(geom) ) { case POINTTYPE: return lwpoint_force_geodetic((LWPOINT *)geom); case LINETYPE: return lwline_force_geodetic((LWLINE *)geom); case POLYGONTYPE: return lwpoly_force_geodetic((LWPOLY *)geom); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return lwcollection_force_geodetic((LWCOLLECTION *)geom); default: lwerror("unsupported input geometry type: %d", lwgeom_get_type(geom)); } return LW_FALSE; } double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s) { GEOGRAPHIC_POINT a, b; double za = 0.0, zb = 0.0; POINT4D p; uint32_t i; int hasz = LW_FALSE; double length = 0.0; double seglength = 0.0; /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 2 ) return 0.0; /* See if we have a third dimension */ hasz = FLAGS_GET_Z(pa->flags); /* Initialize first point */ getPoint4d_p(pa, 0, &p); geographic_point_init(p.x, p.y, &a); if ( hasz ) za = p.z; /* Loop and sum the length for each segment */ for ( i = 1; i < pa->npoints; i++ ) { seglength = 0.0; getPoint4d_p(pa, i, &p); geographic_point_init(p.x, p.y, &b); if ( hasz ) zb = p.z; /* Special sphere case */ if ( s->a == s->b ) seglength = s->radius * sphere_distance(&a, &b); /* Spheroid case */ else seglength = spheroid_distance(&a, &b, s); /* Add in the vertical displacement if we're in 3D */ if ( hasz ) seglength = sqrt( (zb-za)*(zb-za) + seglength*seglength ); /* Add this segment length to the total */ length += seglength; /* B gets incremented in the next loop, so we save the value here */ a = b; za = zb; } return length; } double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s) { int type; uint32_t i = 0; double length = 0.0; assert(geom); /* No area in nothing */ if ( lwgeom_is_empty(geom) ) return 0.0; type = geom->type; if ( type == POINTTYPE || type == MULTIPOINTTYPE ) return 0.0; if ( type == LINETYPE ) return ptarray_length_spheroid(((LWLINE*)geom)->points, s); if ( type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { length += ptarray_length_spheroid(poly->rings[i], s); } return length; } if ( type == TRIANGLETYPE ) return ptarray_length_spheroid(((LWTRIANGLE*)geom)->points, s); if ( lwtype_is_collection( type ) ) { LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) { length += lwgeom_length_spheroid(col->geoms[i], s); } return length; } lwerror("unsupported type passed to lwgeom_length_sphere"); return 0.0; } /** * When features are snapped or sometimes they are just this way, they are very close to * the geodetic bounds but slightly over. This routine nudges those points, and only * those points, back over to the bounds. * http://trac.osgeo.org/postgis/ticket/1292 */ static int ptarray_nudge_geodetic(POINTARRAY *pa) { uint32_t i; POINT4D p; int altered = LW_FALSE; int rv = LW_FALSE; static double tolerance = 1e-10; if ( ! pa ) lwerror("ptarray_nudge_geodetic called with null input"); for(i = 0; i < pa->npoints; i++ ) { getPoint4d_p(pa, i, &p); if ( p.x < -180.0 && (-180.0 - p.x < tolerance) ) { p.x = -180.0; altered = LW_TRUE; } if ( p.x > 180.0 && (p.x - 180.0 < tolerance) ) { p.x = 180.0; altered = LW_TRUE; } if ( p.y < -90.0 && (-90.0 - p.y < tolerance) ) { p.y = -90.0; altered = LW_TRUE; } if ( p.y > 90.0 && (p.y - 90.0 < tolerance) ) { p.y = 90.0; altered = LW_TRUE; } if ( altered == LW_TRUE ) { ptarray_set_point4d(pa, i, &p); altered = LW_FALSE; rv = LW_TRUE; } } return rv; } /** * When features are snapped or sometimes they are just this way, they are very close to * the geodetic bounds but slightly over. This routine nudges those points, and only * those points, back over to the bounds. * http://trac.osgeo.org/postgis/ticket/1292 */ int lwgeom_nudge_geodetic(LWGEOM *geom) { int type; uint32_t i = 0; int rv = LW_FALSE; assert(geom); /* No points in nothing */ if ( lwgeom_is_empty(geom) ) return LW_FALSE; type = geom->type; if ( type == POINTTYPE ) return ptarray_nudge_geodetic(((LWPOINT*)geom)->point); if ( type == LINETYPE ) return ptarray_nudge_geodetic(((LWLINE*)geom)->points); if ( type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { int n = ptarray_nudge_geodetic(poly->rings[i]); rv = (rv == LW_TRUE ? rv : n); } return rv; } if ( type == TRIANGLETYPE ) return ptarray_nudge_geodetic(((LWTRIANGLE*)geom)->points); if ( lwtype_is_collection( type ) ) { LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) { int n = lwgeom_nudge_geodetic(col->geoms[i]); rv = (rv == LW_TRUE ? rv : n); } return rv; } lwerror("unsupported type (%s) passed to lwgeom_nudge_geodetic", lwtype_name(type)); return rv; } /** * Utility function for checking if P is within the cone defined by A1/A2. */ static int point_in_cone(const POINT3D *A1, const POINT3D *A2, const POINT3D *P) { POINT3D AC; /* Center point of A1/A2 */ double min_similarity, similarity; /* Boundary case */ if (point3d_equals(A1, P) || point3d_equals(A2, P)) return LW_TRUE; /* The normalized sum bisects the angle between start and end. */ vector_sum(A1, A2, &AC); normalize(&AC); /* The projection of start onto the center defines the minimum similarity */ min_similarity = dot_product(A1, &AC); /* If the edge is sufficiently curved, use the dot product test */ if (fabs(1.0 - min_similarity) > 1e-10) { /* The projection of candidate p onto the center */ similarity = dot_product(P, &AC); /* If the projection of the candidate is larger than */ /* the projection of the start point, the candidate */ /* must be closer to the center than the start, so */ /* therefor inside the cone */ if (similarity > min_similarity) { return LW_TRUE; } else { return LW_FALSE; } } else { /* Where the edge is very narrow, the dot product test */ /* fails, but we can use the almost-planar nature of the */ /* problem space then to test if the vector from the */ /* candidate to the start point in a different direction */ /* to the vector from candidate to end point */ /* If so, then candidate is between start and end */ POINT3D PA1, PA2; vector_difference(P, A1, &PA1); vector_difference(P, A2, &PA2); normalize(&PA1); normalize(&PA2); if (dot_product(&PA1, &PA2) < 0.0) { return LW_TRUE; } else { return LW_FALSE; } } return LW_FALSE; } /** * Utility function for edge_intersects(), signum with a tolerance * in determining if the value is zero. */ static int dot_product_side(const POINT3D *p, const POINT3D *q) { double dp = dot_product(p, q); if ( FP_IS_ZERO(dp) ) return 0; return dp < 0.0 ? -1 : 1; } /** * Returns non-zero if edges A and B interact. The type of interaction is given in the * return value with the bitmask elements defined above. */ uint32_t edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2) { POINT3D AN, BN, VN; /* Normals to plane A and plane B */ double ab_dot; int a1_side, a2_side, b1_side, b2_side; int rv = PIR_NO_INTERACT; /* Normals to the A-plane and B-plane */ unit_normal(A1, A2, &AN); unit_normal(B1, B2, &BN); /* Are A-plane and B-plane basically the same? */ ab_dot = dot_product(&AN, &BN); if ( FP_EQUALS(fabs(ab_dot), 1.0) ) { /* Co-linear case */ if ( point_in_cone(A1, A2, B1) || point_in_cone(A1, A2, B2) || point_in_cone(B1, B2, A1) || point_in_cone(B1, B2, A2) ) { rv |= PIR_INTERSECTS; rv |= PIR_COLINEAR; } return rv; } /* What side of plane-A and plane-B do the end points */ /* of A and B fall? */ a1_side = dot_product_side(&BN, A1); a2_side = dot_product_side(&BN, A2); b1_side = dot_product_side(&AN, B1); b2_side = dot_product_side(&AN, B2); /* Both ends of A on the same side of plane B. */ if ( a1_side == a2_side && a1_side != 0 ) { /* No intersection. */ return PIR_NO_INTERACT; } /* Both ends of B on the same side of plane A. */ if ( b1_side == b2_side && b1_side != 0 ) { /* No intersection. */ return PIR_NO_INTERACT; } /* A straddles B and B straddles A, so... */ if ( a1_side != a2_side && (a1_side + a2_side) == 0 && b1_side != b2_side && (b1_side + b2_side) == 0 ) { /* Have to check if intersection point is inside both arcs */ unit_normal(&AN, &BN, &VN); if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) ) { return PIR_INTERSECTS; } /* Have to check if intersection point is inside both arcs */ vector_scale(&VN, -1); if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) ) { return PIR_INTERSECTS; } return PIR_NO_INTERACT; } /* The rest are all intersects variants... */ rv |= PIR_INTERSECTS; /* A touches B */ if ( a1_side == 0 ) { /* Touches at A1, A2 is on what side? */ rv |= (a2_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT); } else if ( a2_side == 0 ) { /* Touches at A2, A1 is on what side? */ rv |= (a1_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT); } /* B touches A */ if ( b1_side == 0 ) { /* Touches at B1, B2 is on what side? */ rv |= (b2_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT); } else if ( b2_side == 0 ) { /* Touches at B2, B1 is on what side? */ rv |= (b1_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT); } return rv; } /** * This routine returns LW_TRUE if the stabline joining the pt_outside and pt_to_test * crosses the ring an odd number of times, or if the pt_to_test is on the ring boundary itself, * returning LW_FALSE otherwise. * The pt_outside *must* be guaranteed to be outside the ring (use the geography_pt_outside() function * to derive one in postgis, or the gbox_pt_outside() function if you don't mind burning CPU cycles * building a gbox first). */ int ptarray_contains_point_sphere(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test) { POINT3D S1, S2; /* Stab line end points */ POINT3D E1, E2; /* Edge end points (3-space) */ POINT2D p; /* Edge end points (lon/lat) */ uint32_t count = 0, i, inter; /* Null input, not enough points for a ring? You ain't closed! */ if ( ! pa || pa->npoints < 4 ) return LW_FALSE; /* Set up our stab line */ ll2cart(pt_to_test, &S1); ll2cart(pt_outside, &S2); /* Initialize first point */ getPoint2d_p(pa, 0, &p); ll2cart(&p, &E1); /* Walk every edge and see if the stab line hits it */ for ( i = 1; i < pa->npoints; i++ ) { LWDEBUGF(4, "testing edge (%d)", i); LWDEBUGF(4, " start point == POINT(%.12g %.12g)", p.x, p.y); /* Read next point. */ getPoint2d_p(pa, i, &p); ll2cart(&p, &E2); /* Skip over too-short edges. */ if ( point3d_equals(&E1, &E2) ) { continue; } /* Our test point is on an edge end! Point is "in ring" by our definition */ if ( point3d_equals(&S1, &E1) ) { return LW_TRUE; } /* Calculate relationship between stab line and edge */ inter = edge_intersects(&S1, &S2, &E1, &E2); /* We have some kind of interaction... */ if ( inter & PIR_INTERSECTS ) { /* If the stabline is touching the edge, that implies the test point */ /* is on the edge, so we're done, the point is in (on) the ring. */ if ( (inter & PIR_A_TOUCH_RIGHT) || (inter & PIR_A_TOUCH_LEFT) ) { return LW_TRUE; } /* It's a touching interaction, disregard all the left-side ones. */ /* It's a co-linear intersection, ignore those. */ if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR ) { /* Do nothing, to avoid double counts. */ LWDEBUGF(4," edge (%d) crossed, disregarding to avoid double count", i, count); } else { /* Increment crossingn count. */ count++; LWDEBUGF(4," edge (%d) crossed, count == %d", i, count); } } else { LWDEBUGF(4," edge (%d) did not cross", i); } /* Increment to next edge */ E1 = E2; } LWDEBUGF(4,"final count == %d", count); /* An odd number of crossings implies containment! */ if ( count % 2 ) { return LW_TRUE; } return LW_FALSE; } lwgeom/src/liblwgeom/lwout_x3d.c0000644000176200001440000003743313773172540016433 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011-2017 Arrival 3D, Regina Obe * **********************************************************************/ /** * @file X3D output routines. * **********************************************************************/ #include "lwout_x3d.h" /* * VERSION X3D 3.0.2 http://www.web3d.org/specifications/x3d-3.0.dtd */ /* takes a GEOMETRY and returns a X3D representation */ char* lwgeom_to_x3d3(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid) { stringbuffer_t *sb; int rv; char *result; /* Empty string for empties */ if( lwgeom_is_empty(geom) ) { char *ret = NULL; ret = lwalloc(1); ret[0] = '\0'; return ret; } sb = stringbuffer_create(); rv = lwgeom_to_x3d3_sb(geom, srs, precision, opts, defid, sb); if ( rv == LW_FAILURE ) { stringbuffer_destroy(sb); return NULL; } result = stringbuffer_getstringcopy(sb); stringbuffer_destroy(sb); return result; } /* takes a GEOMETRY and appends to string buffer the x3d output */ static int lwgeom_to_x3d3_sb(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb) { int type = geom->type; switch (type) { case POINTTYPE: return asx3d3_point_sb((LWPOINT*)geom, srs, precision, opts, defid, sb); case LINETYPE: return asx3d3_line_sb((LWLINE*)geom, srs, precision, opts, defid, sb); case POLYGONTYPE: { /** We might change this later, but putting a polygon in an indexed face set * seems like the simplest way to go so treat just like a mulitpolygon */ LWCOLLECTION *tmp = (LWCOLLECTION*)lwgeom_as_multi(geom); asx3d3_multi_sb(tmp, srs, precision, opts, defid, sb); lwcollection_free(tmp); return LW_SUCCESS; } case TRIANGLETYPE: return asx3d3_triangle_sb((LWTRIANGLE*)geom, srs, precision, opts, defid, sb); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: return asx3d3_multi_sb((LWCOLLECTION*)geom, srs, precision, opts, defid, sb); case POLYHEDRALSURFACETYPE: return asx3d3_psurface_sb((LWPSURFACE*)geom, srs, precision, opts, defid, sb); case TINTYPE: return asx3d3_tin_sb((LWTIN*)geom, srs, precision, opts, defid, sb); case COLLECTIONTYPE: return asx3d3_collection_sb((LWCOLLECTION*)geom, srs, precision, opts, defid, sb); default: lwerror("lwgeom_to_x3d3: '%s' geometry type not supported", lwtype_name(type)); return LW_FAILURE; } } static int asx3d3_point_sb(const LWPOINT *point, __attribute__((__unused__)) char *srs, int precision, int opts, __attribute__((__unused__)) const char *defid, stringbuffer_t *sb) { /** for point we just output the coordinates **/ return ptarray_to_x3d3_sb(point->point, precision, opts, 0, sb); } static int asx3d3_line_coords_sb(const LWLINE *line, int precision, int opts, stringbuffer_t *sb) { return ptarray_to_x3d3_sb(line->points, precision, opts, lwline_is_closed(line), sb); } /* Calculate the coordIndex property of the IndexedLineSet for the multilinestring and add to string buffer */ static int asx3d3_mline_coordindex_sb(const LWMLINE *mgeom, stringbuffer_t *sb) { LWLINE *geom; uint32_t i, j, k, si; POINTARRAY *pa; uint32_t np; j = 0; for (i=0; i < mgeom->ngeoms; i++) { geom = (LWLINE *) mgeom->geoms[i]; pa = geom->points; np = pa->npoints; si = j; /* start index of first point of linestring */ for (k=0; k < np ; k++) { if (k) { stringbuffer_aprintf(sb, " "); } /** if the linestring is closed, we put the start point index * for the last vertex to denote use first point * and don't increment the index **/ if (!lwline_is_closed(geom) || k < (np -1) ) { stringbuffer_aprintf(sb, "%u", j); j += 1; } else { stringbuffer_aprintf(sb,"%u", si); } } if (i < (mgeom->ngeoms - 1) ) { stringbuffer_aprintf(sb, " -1 "); /* separator for each linestring */ } } return LW_SUCCESS; } /* Calculate the coordIndex property of the IndexedLineSet for a multipolygon This is not ideal -- would be really nice to just share this function with psurf, but I'm not smart enough to do that yet*/ static int asx3d3_mpoly_coordindex_sb(const LWMPOLY *psur, stringbuffer_t *sb) { LWPOLY *patch; uint32_t i, j, k, l; uint32_t np; j = 0; for (i=0; ingeoms; i++) { patch = (LWPOLY *) psur->geoms[i]; for (l=0; l < patch->nrings; l++) { np = patch->rings[l]->npoints - 1; for (k=0; k < np ; k++) { if (k) { stringbuffer_aprintf(sb, " "); } stringbuffer_aprintf(sb, "%d", (j + k)); } j += k; if (l < (patch->nrings - 1) ) { /** @todo TODO: Decide the best way to render holes * Evidently according to my X3D expert the X3D consortium doesn't really * support holes and it's an issue of argument among many that feel it should. He thinks CAD x3d extensions to spec might. * What he has done and others developing X3D exports to simulate a hole is to cut around it. * So if you have a donut, you would cut it into half and have 2 solid polygons. Not really sure the best way to handle this. * For now will leave it as polygons stacked on top of each other -- which is what we are doing here and perhaps an option * to color differently. It's not ideal but the alternative sounds complicated. **/ stringbuffer_aprintf(sb, " -1 "); /* separator for each inner ring. Ideally we should probably triangulate and cut around as others do */ } } if (i < (psur->ngeoms - 1) ) { stringbuffer_aprintf(sb, " -1 "); /* separator for each subgeom */ } } return LW_SUCCESS; } /** Return the linestring as an X3D LineSet */ static int asx3d3_line_sb(const LWLINE *line, __attribute__((__unused__)) char *srs, int precision, int opts, __attribute__((__unused__)) const char *defid, stringbuffer_t *sb) { /* int dimension=2; */ POINTARRAY *pa; /* if (FLAGS_GET_Z(line->flags)) dimension = 3; */ pa = line->points; stringbuffer_aprintf(sb, "", defid, pa->npoints); if ( X3D_USE_GEOCOORDS(opts) ) stringbuffer_aprintf(sb, "points, precision, opts, lwline_is_closed((LWLINE *) line), sb); stringbuffer_aprintf(sb, "' />"); return stringbuffer_aprintf(sb, ""); } /** Compute the X3D coordinates of the polygon and add to string buffer **/ static int asx3d3_poly_sb(const LWPOLY *poly, __attribute__((__unused__)) char *srs, int precision, int opts, __attribute__((__unused__)) int is_patch, __attribute__((__unused__)) const char *defid, stringbuffer_t *sb) { uint32_t i; for (i=0; inrings; i++) { if (i) stringbuffer_aprintf(sb, " "); /* inner ring points start */ ptarray_to_x3d3_sb(poly->rings[i], precision, opts, 1, sb); } return LW_SUCCESS; } static int asx3d3_triangle_sb(const LWTRIANGLE *triangle, __attribute__((__unused__)) char *srs, int precision, int opts, __attribute__((__unused__)) const char *defid, stringbuffer_t *sb) { return ptarray_to_x3d3_sb(triangle->points, precision, opts, 1, sb); } /* * Don't call this with single-geoms inspected! */ static int asx3d3_multi_sb(const LWCOLLECTION *col, __attribute__((__unused__)) char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb) { char *x3dtype; uint32_t i; int dimension=2; if (FLAGS_GET_Z(col->flags)) dimension = 3; LWGEOM *subgeom; x3dtype=""; switch (col->type) { case MULTIPOINTTYPE: x3dtype = "PointSet"; if ( dimension == 2 ){ /** Use Polypoint2D instead **/ x3dtype = "Polypoint2D"; stringbuffer_aprintf(sb, "<%s %s point='", x3dtype, defid); } else { stringbuffer_aprintf(sb, "<%s %s>", x3dtype, defid); } break; case MULTILINETYPE: x3dtype = "IndexedLineSet"; stringbuffer_aprintf(sb, "<%s %s coordIndex='", x3dtype, defid); asx3d3_mline_coordindex_sb((const LWMLINE *)col, sb); stringbuffer_aprintf(sb, "'>"); break; case MULTIPOLYGONTYPE: x3dtype = "IndexedFaceSet"; stringbuffer_aprintf(sb, "<%s %s convex='false' coordIndex='", x3dtype, defid); asx3d3_mpoly_coordindex_sb((const LWMPOLY *)col, sb); stringbuffer_aprintf(sb, "'>"); break; default: lwerror("asx3d3_multi_buf: '%s' geometry type not supported", lwtype_name(col->type)); return 0; } if (dimension == 3){ if ( X3D_USE_GEOCOORDS(opts) ) stringbuffer_aprintf(sb, "ngeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == POINTTYPE) { asx3d3_point_sb((LWPOINT*)subgeom, 0, precision, opts, defid, sb); stringbuffer_aprintf(sb, " "); } else if (subgeom->type == LINETYPE) { asx3d3_line_coords_sb((LWLINE*)subgeom, precision, opts, sb); stringbuffer_aprintf(sb, " "); } else if (subgeom->type == POLYGONTYPE) { asx3d3_poly_sb((LWPOLY*)subgeom, 0, precision, opts, 0, defid, sb); stringbuffer_aprintf(sb, " "); } } /* Close outmost tag */ if (dimension == 3){ stringbuffer_aprintf(sb, "' />", x3dtype); } else { stringbuffer_aprintf(sb, "' />"); } return LW_SUCCESS; } /* * Don't call this with single-geoms inspected! */ static int asx3d3_psurface_sb(const LWPSURFACE *psur, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb) { uint32_t i; uint32_t j; uint32_t k; uint32_t np; LWPOLY *patch; /* Open outmost tag */ stringbuffer_aprintf(sb, ""); } /* * Computes X3D representation of TIN (as IndexedTriangleSet and adds to string buffer) */ static int asx3d3_tin_sb(const LWTIN *tin, __attribute__((__unused__)) char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb) { uint32_t i; uint32_t k; /* int dimension=2; */ stringbuffer_aprintf(sb,""); } static int asx3d3_collection_sb(const LWCOLLECTION *col, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb) { uint32_t i; LWGEOM *subgeom; /* Open outmost tag */ /** @TODO: if collection should be grouped, we'll wrap in a group tag. Still needs cleanup * like the shapes should really be in a transform **/ #ifdef PGIS_X3D_OUTERMOST_TAGS stringbuffer_aprintf(sb, "<%sGroup>", defid); #endif for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; stringbuffer_aprintf(sb, "", defid); if ( subgeom->type == POINTTYPE ) { asx3d3_point_sb((LWPOINT*)subgeom, 0, precision, opts, defid, sb); } else if ( subgeom->type == LINETYPE ) { asx3d3_line_sb((LWLINE*)subgeom, 0, precision, opts, defid, sb); } else if ( subgeom->type == POLYGONTYPE ) { asx3d3_poly_sb((LWPOLY*)subgeom, 0, precision, opts, 0, defid, sb); } else if ( subgeom->type == TINTYPE ) { asx3d3_tin_sb((LWTIN*)subgeom, srs, precision, opts, defid, sb); } else if ( subgeom->type == POLYHEDRALSURFACETYPE ) { asx3d3_psurface_sb((LWPSURFACE*)subgeom, srs, precision, opts, defid, sb); } else if ( lwgeom_is_collection(subgeom) ) { if ( subgeom->type == COLLECTIONTYPE ) asx3d3_collection_sb((LWCOLLECTION*)subgeom, 0, precision, opts, defid, sb); else asx3d3_multi_sb((LWCOLLECTION*)subgeom, 0, precision, opts, defid, sb); } else lwerror("asx3d3_collection_buf: unknown geometry type"); stringbuffer_aprintf(sb, ""); } /* Close outmost tag */ #ifdef PGIS_X3D_OUTERMOST_TAGS stringbuffer_aprintf(sb, "", defid); #endif return LW_SUCCESS; } /** In X3D3, coordinates are separated by a space separator */ static int ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, stringbuffer_t *sb ) { uint32_t i; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; char z[OUT_DOUBLE_BUFFER_SIZE]; if ( ! FLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/ if ( !is_closed || i < (pa->npoints - 1) ) { POINT2D pt; getPoint2d_p(pa, i, &pt); lwprint_double( pt.x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt.y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if ( i ) stringbuffer_append(sb," "); if ( ( opts & LW_X3D_FLIP_XY) ) stringbuffer_aprintf(sb, "%s %s", y, x); else stringbuffer_aprintf(sb, "%s %s", x, y); } } } else { for (i=0; inpoints; i++) { /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/ if ( !is_closed || i < (pa->npoints - 1) ) { POINT4D pt; getPoint4d_p(pa, i, &pt); lwprint_double( pt.x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt.y, precision, y, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt.z, precision, z, OUT_DOUBLE_BUFFER_SIZE); if ( i ) stringbuffer_append(sb," "); if ( ( opts & LW_X3D_FLIP_XY) ) stringbuffer_aprintf(sb, "%s %s %s", y, x, z); else stringbuffer_aprintf(sb, "%s %s %s", x, y, z); } } } return LW_SUCCESS; } lwgeom/src/liblwgeom/lwgeom_wrapx.c0000644000176200001440000001404313773172540017206 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2016 Sandro Santilli * **********************************************************************/ #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "lwgeom_geos.h" #include "liblwgeom_internal.h" #include #include LWGEOM* lwgeom_wrapx(const LWGEOM* lwgeom_in, double cutx, double amount); static LWCOLLECTION* lwcollection_wrapx(const LWCOLLECTION* lwcoll_in, double cutx, double amount); static LWGEOM* lwgeom_split_wrapx(const LWGEOM* geom_in, double cutx, double amount) { LWGEOM *blade, *split; POINTARRAY *bladepa; POINT4D pt; const GBOX *box_in; AFFINE affine = { 1, 0, 0, 0, 1, 0, 0, 0, 1, amount, 0, 0, }; /* Extract box */ /* TODO: check if the bbox should be force-recomputed */ box_in = lwgeom_get_bbox(geom_in); if ( ! box_in ) { /* must be empty */ return lwgeom_clone_deep(geom_in); } LWDEBUGF(2, "BOX X range is %g..%g, cutx:%g, amount:%g", box_in->xmin, box_in->xmax, cutx, amount); /* Check if geometry is fully on the side needing shift */ if ( ( amount < 0 && box_in->xmin >= cutx ) || ( amount > 0 && box_in->xmax <= cutx ) ) { split = lwgeom_clone_deep(geom_in); lwgeom_affine(split, &affine); LWDEBUGG(2, split, "returning the translated geometry"); return split; } /* Check if geometry is fully on the side needing no shift */ if ( ( amount < 0 && box_in->xmax <= cutx ) || ( amount > 0 && box_in->xmin >= cutx ) ) { split = lwgeom_clone_deep(geom_in); LWDEBUGG(2, split, "returning the cloned geometry"); return split; } /* We need splitting here */ /* construct blade */ bladepa = ptarray_construct(0, 0, 2); pt.x = cutx; pt.y = box_in->ymin - 1; ptarray_set_point4d(bladepa, 0, &pt); pt.y = box_in->ymax + 1; ptarray_set_point4d(bladepa, 1, &pt); blade = lwline_as_lwgeom(lwline_construct(geom_in->srid, NULL, bladepa)); LWDEBUG(2, "splitting the geometry"); /* split by blade */ split = lwgeom_split(geom_in, blade); lwgeom_free(blade); if ( ! split ) { lwerror("%s:%d - lwgeom_split_wrapx: %s", __FILE__, __LINE__, lwgeom_geos_errmsg); return NULL; } LWDEBUGG(2, split, "split geometry"); /* iterate over components, translate if needed */ const LWCOLLECTION *col = lwgeom_as_lwcollection(split); if ( ! col ) { /* not split, this is unexpected */ lwnotice("WARNING: unexpected lack of split in lwgeom_split_wrapx"); return lwgeom_clone_deep(geom_in); } LWCOLLECTION *col_out = lwcollection_wrapx(col, cutx, amount); lwgeom_free(split); /* unary-union the result (homogenize too ?) */ LWGEOM* out = lwgeom_unaryunion(lwcollection_as_lwgeom(col_out)); LWDEBUGF(2, "col_out:%p, unaryunion_out:%p", col_out, out); LWDEBUGG(2, out, "unary-unioned"); lwcollection_free(col_out); return out; } static LWCOLLECTION* lwcollection_wrapx(const LWCOLLECTION* lwcoll_in, double cutx, double amount) { LWGEOM** wrap_geoms; LWCOLLECTION* out; uint32_t i; int outtype = lwcoll_in->type; wrap_geoms = lwalloc(lwcoll_in->ngeoms * sizeof(LWGEOM*)); if ( ! wrap_geoms ) { lwerror("Out of virtual memory"); return NULL; } for (i=0; ingeoms; ++i) { LWDEBUGF(3, "Wrapping collection element %d", i); wrap_geoms[i] = lwgeom_wrapx(lwcoll_in->geoms[i], cutx, amount); /* an exception should prevent this from ever returning NULL */ if ( ! wrap_geoms[i] ) { uint32_t j; lwnotice("Error wrapping geometry, cleaning up"); for (j = 0; j < i; j++) { lwnotice("cleaning geometry %d (%p)", j, wrap_geoms[j]); lwgeom_free(wrap_geoms[j]); } lwfree(wrap_geoms); lwnotice("cleanup complete"); return NULL; } if ( outtype != COLLECTIONTYPE ) { if ( MULTITYPE[wrap_geoms[i]->type] != outtype ) { outtype = COLLECTIONTYPE; } } } /* Now wrap_geoms has wrap_geoms_size geometries */ out = lwcollection_construct(outtype, lwcoll_in->srid, NULL, lwcoll_in->ngeoms, wrap_geoms); return out; } /* exported */ LWGEOM* lwgeom_wrapx(const LWGEOM* lwgeom_in, double cutx, double amount) { /* Nothing to wrap in an empty geom */ if ( lwgeom_is_empty(lwgeom_in) ) { LWDEBUG(2, "geom is empty, cloning"); return lwgeom_clone_deep(lwgeom_in); } /* Nothing to wrap if shift amount is zero */ if ( amount == 0 ) { LWDEBUG(2, "amount is zero, cloning"); return lwgeom_clone_deep(lwgeom_in); } switch (lwgeom_in->type) { case LINETYPE: case POLYGONTYPE: LWDEBUG(2, "split-wrapping line or polygon"); return lwgeom_split_wrapx(lwgeom_in, cutx, amount); case POINTTYPE: { const LWPOINT *pt = lwgeom_as_lwpoint(lwgeom_clone_deep(lwgeom_in)); POINT4D pt4d; getPoint4d_p(pt->point, 0, &pt4d); LWDEBUGF(2, "POINT X is %g, cutx:%g, amount:%g", pt4d.x, cutx, amount); if ( ( amount < 0 && pt4d.x > cutx ) || ( amount > 0 && pt4d.x < cutx ) ) { pt4d.x += amount; ptarray_set_point4d(pt->point, 0, &pt4d); } return lwpoint_as_lwgeom(pt); } case MULTIPOINTTYPE: case MULTIPOLYGONTYPE: case MULTILINETYPE: case COLLECTIONTYPE: LWDEBUG(2, "collection-wrapping multi"); return lwcollection_as_lwgeom( lwcollection_wrapx((const LWCOLLECTION*)lwgeom_in, cutx, amount) ); default: lwerror("Wrapping of %s geometries is unsupported", lwtype_name(lwgeom_in->type)); return NULL; } } lwgeom/src/liblwgeom/lwout_kml.c0000644000176200001440000001565013773172540016515 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2006 Corporacion Autonoma Regional de Santander * Copyright 2010 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "stringbuffer.h" static int lwgeom_to_kml2_sb(const LWGEOM *geom, int precision, const char *prefix, stringbuffer_t *sb); static int lwpoint_to_kml2_sb(const LWPOINT *point, int precision, const char *prefix, stringbuffer_t *sb); static int lwline_to_kml2_sb(const LWLINE *line, int precision, const char *prefix, stringbuffer_t *sb); static int lwtriangle_to_kml2_sb(const LWTRIANGLE *tri, int precision, const char *prefix, stringbuffer_t *sb); static int lwpoly_to_kml2_sb(const LWPOLY *poly, int precision, const char *prefix, stringbuffer_t *sb); static int lwcollection_to_kml2_sb(const LWCOLLECTION *col, int precision, const char *prefix, stringbuffer_t *sb); static int ptarray_to_kml2_sb(const POINTARRAY *pa, int precision, stringbuffer_t *sb); /* * KML 2.2.0 */ /* takes a GEOMETRY and returns a KML representation */ char* lwgeom_to_kml2(const LWGEOM *geom, int precision, const char *prefix) { stringbuffer_t *sb; int rv; char *kml; /* Can't do anything with empty */ if( lwgeom_is_empty(geom) ) return NULL; sb = stringbuffer_create(); rv = lwgeom_to_kml2_sb(geom, precision, prefix, sb); if ( rv == LW_FAILURE ) { stringbuffer_destroy(sb); return NULL; } kml = stringbuffer_getstringcopy(sb); stringbuffer_destroy(sb); return kml; } static int lwgeom_to_kml2_sb(const LWGEOM *geom, int precision, const char *prefix, stringbuffer_t *sb) { switch (geom->type) { case POINTTYPE: return lwpoint_to_kml2_sb((LWPOINT*)geom, precision, prefix, sb); case LINETYPE: return lwline_to_kml2_sb((LWLINE*)geom, precision, prefix, sb); case TRIANGLETYPE: return lwtriangle_to_kml2_sb((LWTRIANGLE *)geom, precision, prefix, sb); case POLYGONTYPE: return lwpoly_to_kml2_sb((LWPOLY*)geom, precision, prefix, sb); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case TINTYPE: return lwcollection_to_kml2_sb((LWCOLLECTION*)geom, precision, prefix, sb); default: lwerror("lwgeom_to_kml2: '%s' geometry type not supported", lwtype_name(geom->type)); return LW_FAILURE; } } static int ptarray_to_kml2_sb(const POINTARRAY *pa, int precision, stringbuffer_t *sb) { uint32_t i, j; uint32_t dims = FLAGS_GET_Z(pa->flags) ? 3 : 2; POINT4D pt; double *d; for ( i = 0; i < pa->npoints; i++ ) { getPoint4d_p(pa, i, &pt); d = (double*)(&pt); if ( i ) stringbuffer_append(sb," "); for (j = 0; j < dims; j++) { if ( j ) stringbuffer_append(sb,","); if( fabs(d[j]) < OUT_MAX_DOUBLE ) { if ( stringbuffer_aprintf(sb, "%.*f", precision, d[j]) < 0 ) return LW_FAILURE; } else { if ( stringbuffer_aprintf(sb, "%g", d[j]) < 0 ) return LW_FAILURE; } stringbuffer_trim_trailing_zeroes(sb); } } return LW_SUCCESS; } static int lwpoint_to_kml2_sb(const LWPOINT *point, int precision, const char *prefix, stringbuffer_t *sb) { /* Open point */ if ( stringbuffer_aprintf(sb, "<%sPoint><%scoordinates>", prefix, prefix) < 0 ) return LW_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(point->point, precision, sb) == LW_FAILURE ) return LW_FAILURE; /* Close point */ if ( stringbuffer_aprintf(sb, "", prefix, prefix) < 0 ) return LW_FAILURE; return LW_SUCCESS; } static int lwline_to_kml2_sb(const LWLINE *line, int precision, const char *prefix, stringbuffer_t *sb) { /* Open linestring */ if ( stringbuffer_aprintf(sb, "<%sLineString><%scoordinates>", prefix, prefix) < 0 ) return LW_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(line->points, precision, sb) == LW_FAILURE ) return LW_FAILURE; /* Close linestring */ if ( stringbuffer_aprintf(sb, "", prefix, prefix) < 0 ) return LW_FAILURE; return LW_SUCCESS; } static int lwtriangle_to_kml2_sb(const LWTRIANGLE *tri, int precision, const char *prefix, stringbuffer_t *sb) { /* Open polygon */ if (stringbuffer_aprintf( sb, "<%sPolygon><%souterBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix, prefix) < 0) return LW_FAILURE; /* Coordinate array */ if (ptarray_to_kml2_sb(tri->points, precision, sb) == LW_FAILURE) return LW_FAILURE; /* Close polygon */ if (stringbuffer_aprintf( sb, "", prefix, prefix, prefix, prefix) < 0) return LW_FAILURE; return LW_SUCCESS; } static int lwpoly_to_kml2_sb(const LWPOLY *poly, int precision, const char *prefix, stringbuffer_t *sb) { uint32_t i; int rv; /* Open polygon */ if ( stringbuffer_aprintf(sb, "<%sPolygon>", prefix) < 0 ) return LW_FAILURE; for ( i = 0; i < poly->nrings; i++ ) { /* Inner or outer ring opening tags */ if( i ) rv = stringbuffer_aprintf(sb, "<%sinnerBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); else rv = stringbuffer_aprintf(sb, "<%souterBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); if ( rv < 0 ) return LW_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(poly->rings[i], precision, sb) == LW_FAILURE ) return LW_FAILURE; /* Inner or outer ring closing tags */ if( i ) rv = stringbuffer_aprintf(sb, "", prefix, prefix, prefix); else rv = stringbuffer_aprintf(sb, "", prefix, prefix, prefix); if ( rv < 0 ) return LW_FAILURE; } /* Close polygon */ if ( stringbuffer_aprintf(sb, "", prefix) < 0 ) return LW_FAILURE; return LW_SUCCESS; } static int lwcollection_to_kml2_sb(const LWCOLLECTION *col, int precision, const char *prefix, stringbuffer_t *sb) { uint32_t i; int rv; /* Open geometry */ if ( stringbuffer_aprintf(sb, "<%sMultiGeometry>", prefix) < 0 ) return LW_FAILURE; for ( i = 0; i < col->ngeoms; i++ ) { rv = lwgeom_to_kml2_sb(col->geoms[i], precision, prefix, sb); if ( rv == LW_FAILURE ) return LW_FAILURE; } /* Close geometry */ if ( stringbuffer_aprintf(sb, "", prefix) < 0 ) return LW_FAILURE; return LW_SUCCESS; } lwgeom/src/liblwgeom/lwin_twkb.c0000644000176200001440000003731013773172540016475 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2014 Nicklas Avén * **********************************************************************/ #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "varint.h" #define TWKB_IN_MAXCOORDS 4 /** * Used for passing the parse state between the parsing functions. */ typedef struct { /* Pointers to the bytes */ const uint8_t *twkb; /* Points to start of TWKB */ const uint8_t *twkb_end; /* Points to end of TWKB */ const uint8_t *pos; /* Current read position */ uint32_t check; /* Simple validity checks on geometries */ uint32_t lwtype; /* Current type we are handling */ uint8_t has_bbox; uint8_t has_size; uint8_t has_idlist; uint8_t has_z; uint8_t has_m; uint8_t is_empty; /* Precision factors to convert ints to double */ double factor; double factor_z; double factor_m; uint64_t size; /* Info about current geometry */ uint8_t magic_byte; /* the magic byte contain info about if twkb contain id, size info, bboxes and precision */ int ndims; /* Number of dimensions */ int64_t *coords; /* An array to keep delta values from 4 dimensions */ } twkb_parse_state; /** * Internal function declarations. */ LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s); /**********************************************************************/ /** * Check that we are not about to read off the end of the WKB * array. */ static inline void twkb_parse_state_advance(twkb_parse_state *s, size_t next) { if( (s->pos + next) > s->twkb_end) { lwerror("%s: TWKB structure does not match expected size!", __func__); // lwnotice("TWKB structure does not match expected size!"); } s->pos += next; } static inline int64_t twkb_parse_state_varint(twkb_parse_state *s) { size_t size; int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size); twkb_parse_state_advance(s, size); return val; } static inline uint64_t twkb_parse_state_uvarint(twkb_parse_state *s) { size_t size; uint64_t val = varint_u64_decode(s->pos, s->twkb_end, &size); twkb_parse_state_advance(s, size); return val; } static inline double twkb_parse_state_double(twkb_parse_state *s, double factor) { size_t size; int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size); twkb_parse_state_advance(s, size); return val / factor; } static inline void twkb_parse_state_varint_skip(twkb_parse_state *s) { size_t size = varint_size(s->pos, s->twkb_end); if ( ! size ) lwerror("%s: no varint to skip", __func__); twkb_parse_state_advance(s, size); return; } static uint32_t lwtype_from_twkb_type(uint8_t twkb_type) { switch (twkb_type) { case 1: return POINTTYPE; case 2: return LINETYPE; case 3: return POLYGONTYPE; case 4: return MULTIPOINTTYPE; case 5: return MULTILINETYPE; case 6: return MULTIPOLYGONTYPE; case 7: return COLLECTIONTYPE; default: /* Error! */ lwerror("Unknown WKB type"); return 0; } return 0; } /** * Byte * Read a byte and advance the parse state forward. */ static uint8_t byte_from_twkb_state(twkb_parse_state *s) { uint8_t val = *(s->pos); twkb_parse_state_advance(s, WKB_BYTE_SIZE); return val; } /** * POINTARRAY * Read a dynamically sized point array and advance the parse state forward. */ static POINTARRAY* ptarray_from_twkb_state(twkb_parse_state *s, uint32_t npoints) { POINTARRAY *pa = NULL; uint32_t ndims = s->ndims; uint32_t i; double *dlist; LWDEBUG(2,"Entering ptarray_from_twkb_state"); LWDEBUGF(4,"Pointarray has %d points", npoints); /* Empty! */ if( npoints == 0 ) return ptarray_construct_empty(s->has_z, s->has_m, 0); pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints; i++ ) { int j = 0; /* X */ s->coords[j] += twkb_parse_state_varint(s); dlist[ndims*i + j] = s->coords[j] / s->factor; j++; /* Y */ s->coords[j] += twkb_parse_state_varint(s); dlist[ndims*i + j] = s->coords[j] / s->factor; j++; /* Z */ if ( s->has_z ) { s->coords[j] += twkb_parse_state_varint(s); dlist[ndims*i + j] = s->coords[j] / s->factor_z; j++; } /* M */ if ( s->has_m ) { s->coords[j] += twkb_parse_state_varint(s); dlist[ndims*i + j] = s->coords[j] / s->factor_m; j++; } } return pa; } /** * POINT */ static LWPOINT* lwpoint_from_twkb_state(twkb_parse_state *s) { static uint32_t npoints = 1; POINTARRAY *pa; LWDEBUG(2,"Entering lwpoint_from_twkb_state"); if ( s->is_empty ) return lwpoint_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); pa = ptarray_from_twkb_state(s, npoints); return lwpoint_construct(SRID_UNKNOWN, NULL, pa); } /** * LINESTRING */ static LWLINE* lwline_from_twkb_state(twkb_parse_state *s) { uint32_t npoints; POINTARRAY *pa; LWDEBUG(2,"Entering lwline_from_twkb_state"); if ( s->is_empty ) return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); /* Read number of points */ npoints = twkb_parse_state_uvarint(s); if ( npoints == 0 ) return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); /* Read coordinates */ pa = ptarray_from_twkb_state(s, npoints); if( pa == NULL ) return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { lwerror("%s must have at least two points", lwtype_name(s->lwtype)); return NULL; } return lwline_construct(SRID_UNKNOWN, NULL, pa); } /** * POLYGON */ static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) { uint32_t nrings; uint32_t i; LWPOLY *poly; LWDEBUG(2,"Entering lwpoly_from_twkb_state"); if ( s->is_empty ) return lwpoly_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); /* Read number of rings */ nrings = twkb_parse_state_uvarint(s); /* Start w/ empty polygon */ poly = lwpoly_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUGF(4,"Polygon has %d rings", nrings); /* Empty polygon? */ if( nrings == 0 ) return poly; for( i = 0; i < nrings; i++ ) { /* Ret number of points */ uint32_t npoints = twkb_parse_state_uvarint(s); POINTARRAY *pa = ptarray_from_twkb_state(s, npoints); /* Skip empty rings */ if( pa == NULL ) continue; /* Force first and last points to be the same. */ if( ! ptarray_is_closed_2d(pa) ) { POINT4D pt; getPoint4d_p(pa, 0, &pt); ptarray_append_point(pa, &pt, LW_FALSE); } /* Check for at least four points. */ if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype)); lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype)); return NULL; } /* Add ring to polygon */ if ( lwpoly_add_ring(poly, pa) == LW_FAILURE ) { LWDEBUG(2, "Unable to add ring to polygon"); lwerror("Unable to add ring to polygon"); } } return poly; } /** * MULTIPOINT */ static LWCOLLECTION* lwmultipoint_from_twkb_state(twkb_parse_state *s) { int ngeoms, i; LWGEOM *geom = NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUG(2,"Entering lwmultipoint_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d", ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(s); } for ( i = 0; i < ngeoms; i++ ) { geom = lwpoint_as_lwgeom(lwpoint_from_twkb_state(s)); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * MULTILINESTRING */ static LWCOLLECTION* lwmultiline_from_twkb_state(twkb_parse_state *s) { int ngeoms, i; LWGEOM *geom = NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUG(2,"Entering lwmultilinestring_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(s); } for ( i = 0; i < ngeoms; i++ ) { geom = lwline_as_lwgeom(lwline_from_twkb_state(s)); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * MULTIPOLYGON */ static LWCOLLECTION* lwmultipoly_from_twkb_state(twkb_parse_state *s) { int ngeoms, i; LWGEOM *geom = NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUG(2,"Entering lwmultipolygon_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(s); } for ( i = 0; i < ngeoms; i++ ) { geom = lwpoly_as_lwgeom(lwpoly_from_twkb_state(s)); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE **/ static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s) { int ngeoms, i; LWGEOM *geom = NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUG(2,"Entering lwcollection_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(s); } for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_twkb_state(s); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } static void header_from_twkb_state(twkb_parse_state *s) { LWDEBUG(2,"Entering magicbyte_from_twkb_state"); uint8_t extended_dims; /* Read the first two bytes */ uint8_t type_precision = byte_from_twkb_state(s); uint8_t metadata = byte_from_twkb_state(s); /* Strip type and precision out of first byte */ uint8_t type = type_precision & 0x0F; int8_t precision = unzigzag8((type_precision & 0xF0) >> 4); /* Convert TWKB type to internal type */ s->lwtype = lwtype_from_twkb_type(type); /* Convert the precision into factor */ s->factor = pow(10, (double)precision); /* Strip metadata flags out of second byte */ s->has_bbox = metadata & 0x01; s->has_size = (metadata & 0x02) >> 1; s->has_idlist = (metadata & 0x04) >> 2; extended_dims = (metadata & 0x08) >> 3; s->is_empty = (metadata & 0x10) >> 4; /* Flag for higher dims means read a third byte */ if ( extended_dims ) { int8_t precision_z, precision_m; extended_dims = byte_from_twkb_state(s); /* Strip Z/M presence and precision from ext byte */ s->has_z = (extended_dims & 0x01); s->has_m = (extended_dims & 0x02) >> 1; precision_z = (extended_dims & 0x1C) >> 2; precision_m = (extended_dims & 0xE0) >> 5; /* Convert the precision into factor */ s->factor_z = pow(10, (double)precision_z); s->factor_m = pow(10, (double)precision_m); } else { s->has_z = 0; s->has_m = 0; s->factor_z = 0; s->factor_m = 0; } /* Read the size, if there is one */ if ( s->has_size ) { s->size = twkb_parse_state_uvarint(s); } /* Calculate the number of dimensions */ s->ndims = 2 + s->has_z + s->has_m; return; } /** * Generic handling for TWKB geometries. The front of every TWKB geometry * (including those embedded in collections) is a type byte and metadata byte, * then optional size, bbox, etc. Read those, then switch to particular type * handling code. */ LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s) { GBOX bbox; LWGEOM *geom = NULL; uint32_t has_bbox = LW_FALSE; int i; /* Read the first two bytes, and optional */ /* extended precision info and optional size info */ header_from_twkb_state(s); /* Just experienced a geometry header, so now we */ /* need to reset our coordinate deltas */ for ( i = 0; i < TWKB_IN_MAXCOORDS; i++ ) { s->coords[i] = 0.0; } /* Read the bounding box, is there is one */ if ( s->has_bbox ) { /* Initialize */ has_bbox = s->has_bbox; memset(&bbox, 0, sizeof(GBOX)); bbox.flags = lwflags(s->has_z, s->has_m, 0); /* X */ bbox.xmin = twkb_parse_state_double(s, s->factor); bbox.xmax = bbox.xmin + twkb_parse_state_double(s, s->factor); /* Y */ bbox.ymin = twkb_parse_state_double(s, s->factor); bbox.ymax = bbox.ymin + twkb_parse_state_double(s, s->factor); /* Z */ if ( s->has_z ) { bbox.zmin = twkb_parse_state_double(s, s->factor_z); bbox.zmax = bbox.zmin + twkb_parse_state_double(s, s->factor_z); } /* M */ if ( s->has_m ) { bbox.mmin = twkb_parse_state_double(s, s->factor_m); bbox.mmax = bbox.mmin + twkb_parse_state_double(s, s->factor_m); } } /* Switch to code for the particular type we're dealing with */ switch( s->lwtype ) { case POINTTYPE: geom = lwpoint_as_lwgeom(lwpoint_from_twkb_state(s)); break; case LINETYPE: geom = lwline_as_lwgeom(lwline_from_twkb_state(s)); break; case POLYGONTYPE: geom = lwpoly_as_lwgeom(lwpoly_from_twkb_state(s)); break; case MULTIPOINTTYPE: geom = lwcollection_as_lwgeom(lwmultipoint_from_twkb_state(s)); break; case MULTILINETYPE: geom = lwcollection_as_lwgeom(lwmultiline_from_twkb_state(s)); break; case MULTIPOLYGONTYPE: geom = lwcollection_as_lwgeom(lwmultipoly_from_twkb_state(s)); break; case COLLECTIONTYPE: geom = lwcollection_as_lwgeom(lwcollection_from_twkb_state(s)); break; /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype)); break; } if ( has_bbox ) geom->bbox = gbox_clone(&bbox); return geom; } /** * WKB inputs *must* have a declared size, to prevent malformed WKB from reading * off the end of the memory segment (this stops a malevolent user from declaring * a one-ring polygon to have 10 rings, causing the WKB reader to walk off the * end of the memory). * * Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD, * LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL */ LWGEOM* lwgeom_from_twkb(const uint8_t *twkb, size_t twkb_size, char check) { int64_t coords[TWKB_IN_MAXCOORDS] = {0, 0, 0, 0}; twkb_parse_state s; LWDEBUG(2,"Entering lwgeom_from_twkb"); LWDEBUGF(4,"twkb_size: %d",(int) twkb_size); /* Zero out the state */ memset(&s, 0, sizeof(twkb_parse_state)); /* Initialize the state appropriately */ s.twkb = s.pos = twkb; s.twkb_end = twkb + twkb_size; s.check = check; s.coords = coords; /* Read the rest of the geometry */ return lwgeom_from_twkb_state(&s); } lwgeom/src/liblwgeom/lwprint.c0000644000176200001440000003350614343212276016172 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2010-2015 Paul Ramsey * Copyright (C) 2011 Sandro Santilli * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" /* Ensures the given lat and lon are in the "normal" range: * -90 to +90 for lat, -180 to +180 for lon. */ static void lwprint_normalize_latlon(double *lat, double *lon) { /* First remove all the truly excessive trips around the world via up or down. */ while (*lat > 270) { *lat -= 360; } while (*lat < -270) { *lat += 360; } /* Now see if latitude is past the top or bottom of the world. * Past 90 or -90 puts us on the other side of the earth, * so wrap latitude and add 180 to longitude to reflect that. */ if (*lat > 90) { *lat = 180 - *lat; *lon += 180; } if (*lat < -90) { *lat = -180 - *lat; *lon += 180; } /* Now make sure lon is in the normal range. Wrapping longitude * has no effect on latitude. */ while (*lon > 180) { *lon -= 360; } while (*lon < -180) { *lon += 360; } } /* Converts a single double to DMS given the specified DMS format string. * Symbols are specified since N/S or E/W are the only differences when printing * lat vs. lon. They are only used if the "C" (compass dir) token appears in the * format string. * NOTE: Format string and symbols are required to be in UTF-8. */ static char * lwdouble_to_dms(double val, const char *pos_dir_symbol, const char *neg_dir_symbol, const char * format) { /* 3 numbers, 1 sign or compass dir, and 5 possible strings (degree signs, spaces, misc text, etc) between or around them.*/ # define NUM_PIECES 9 # define WORK_SIZE 1024 char pieces[NUM_PIECES][WORK_SIZE]; int current_piece = 0; int is_negative = 0; double degrees = 0.0; double minutes = 0.0; double seconds = 0.0; int compass_dir_piece = -1; int reading_deg = 0; int deg_digits = 0; int deg_has_decpoint = 0; int deg_dec_digits = 0; int deg_piece = -1; int reading_min = 0; int min_digits = 0; int min_has_decpoint = 0; int min_dec_digits = 0; int min_piece = -1; int reading_sec = 0; int sec_digits = 0; int sec_has_decpoint = 0; int sec_dec_digits = 0; int sec_piece = -1; int round_pow = 0; int format_length = ((NULL == format) ? 0 : strlen(format)); char * result; int index, following_byte_index; int multibyte_char_width = 1; /* Initialize the working strs to blank. We may not populate all of them, and * this allows us to concat them all at the end without worrying about how many * we actually needed. */ for (index = 0; index < NUM_PIECES; index++) { pieces[index][0] = '\0'; } /* If no format is provided, use a default. */ if (0 == format_length) { /* C2B0 is UTF-8 for the degree symbol. */ format = "D\xC2\xB0""M'S.SSS\"C"; format_length = strlen(format); } else if (format_length > WORK_SIZE) { /* Sanity check, we don't want to overwrite an entire piece of work and no one should need a 1K-sized * format string anyway. */ lwerror("Bad format, exceeds maximum length (%d).", WORK_SIZE); } for (index = 0; index < format_length; index++) { char next_char = format[index]; switch (next_char) { case 'D': if (reading_deg) { /* If we're reading degrees, add another digit. */ deg_has_decpoint ? deg_dec_digits++ : deg_digits++; } else { /* If we're not reading degrees, we are now. */ current_piece++; deg_piece = current_piece; if (deg_digits > 0) { lwerror("Bad format, cannot include degrees (DD.DDD) more than once."); } reading_deg = 1; reading_min = 0; reading_sec = 0; deg_digits++; } break; case 'M': if (reading_min) { /* If we're reading minutes, add another digit. */ min_has_decpoint ? min_dec_digits++ : min_digits++; } else { /* If we're not reading minutes, we are now. */ current_piece++; min_piece = current_piece; if (min_digits > 0) { lwerror("Bad format, cannot include minutes (MM.MMM) more than once."); } reading_deg = 0; reading_min = 1; reading_sec = 0; min_digits++; } break; case 'S': if (reading_sec) { /* If we're reading seconds, add another digit. */ sec_has_decpoint ? sec_dec_digits++ : sec_digits++; } else { /* If we're not reading seconds, we are now. */ current_piece++; sec_piece = current_piece; if (sec_digits > 0) { lwerror("Bad format, cannot include seconds (SS.SSS) more than once."); } reading_deg = 0; reading_min = 0; reading_sec = 1; sec_digits++; } break; case 'C': /* We're done reading anything else we might have been reading. */ if (reading_deg || reading_min || reading_sec) { /* We were reading something, that means this is the next piece. */ reading_deg = 0; reading_min = 0; reading_sec = 0; } current_piece++; if (compass_dir_piece >= 0) { lwerror("Bad format, cannot include compass dir (C) more than once."); } /* The compass dir is a piece all by itself. */ compass_dir_piece = current_piece; current_piece++; break; case '.': /* If we're reading deg, min, or sec, we want a decimal point for it. */ if (reading_deg) { deg_has_decpoint = 1; } else if (reading_min) { min_has_decpoint = 1; } else if (reading_sec) { sec_has_decpoint = 1; } else { /* Not reading anything, just pass through the '.' */ strncat(pieces[current_piece], &next_char, 1); } break; default: /* Any other char is just passed through unchanged. But it does mean we are done reading D, M, or S.*/ if (reading_deg || reading_min || reading_sec) { /* We were reading something, that means this is the next piece. */ current_piece++; reading_deg = 0; reading_min = 0; reading_sec = 0; } /* Check if this is a multi-byte UTF-8 character. If so go ahead and read the rest of the bytes as well. */ multibyte_char_width = 1; if (next_char & 0x80) { if ((next_char & 0xF8) == 0xF0) { multibyte_char_width += 3; } else if ((next_char & 0xF0) == 0xE0) { multibyte_char_width += 2; } else if ((next_char & 0xE0) == 0xC0) { multibyte_char_width += 1; } else { lwerror("Bad format, invalid high-order byte found first, format string may not be UTF-8."); } } if (multibyte_char_width > 1) { if (index + multibyte_char_width >= format_length) { lwerror("Bad format, UTF-8 character first byte found with insufficient following bytes, format string may not be UTF-8."); } for (following_byte_index = (index + 1); following_byte_index < (index + multibyte_char_width); following_byte_index++) { if ((format[following_byte_index] & 0xC0) != 0x80) { lwerror("Bad format, invalid byte found following leading byte of multibyte character, format string may not be UTF-8."); } } } /* Copy all the character's bytes into the current piece. */ strncat(pieces[current_piece], &(format[index]), multibyte_char_width); /* Now increment index past the rest of those bytes. */ index += multibyte_char_width - 1; break; } if (current_piece >= NUM_PIECES) { lwerror("Internal error, somehow needed more pieces than it should."); } } if (deg_piece < 0) { lwerror("Bad format, degrees (DD.DDD) must be included."); } /* Divvy the number up into D, DM, or DMS */ if (val < 0) { val *= -1; is_negative = 1; } degrees = val; if (min_digits > 0) { /* Break degrees to integer and use fraction for minutes */ minutes = modf(val, °rees) * 60; } if (sec_digits > 0) { if (0 == min_digits) { lwerror("Bad format, cannot include seconds (SS.SSS) without including minutes (MM.MMM)."); } seconds = modf(minutes, &minutes) * 60; if (sec_piece >= 0) { /* See if the formatted seconds round up to 60. If so, increment minutes and reset seconds. */ round_pow = pow(10, sec_dec_digits); if (floorf(seconds * round_pow) / round_pow >= 60) { minutes += 1; seconds = 0; } } } /* Handle the compass direction. If not using compass dir, display degrees as a positive/negative number. */ if (compass_dir_piece >= 0) { strcpy(pieces[compass_dir_piece], is_negative ? neg_dir_symbol : pos_dir_symbol); } else if (is_negative) { degrees *= -1; } /* Format the degrees into their string piece. */ if (deg_digits + deg_dec_digits + 2 > WORK_SIZE) { lwerror("Bad format, degrees (DD.DDD) number of digits was greater than our working limit."); } if(deg_piece >= 0) { snprintf(pieces[deg_piece], WORK_SIZE, "%*.*f", deg_digits, deg_dec_digits, degrees); } if (min_piece >= 0) { /* Format the minutes into their string piece. */ if (min_digits + min_dec_digits + 2 > WORK_SIZE) { lwerror("Bad format, minutes (MM.MMM) number of digits was greater than our working limit."); } snprintf(pieces[min_piece], WORK_SIZE, "%*.*f", min_digits, min_dec_digits, minutes); } if (sec_piece >= 0) { /* Format the seconds into their string piece. */ if (sec_digits + sec_dec_digits + 2 > WORK_SIZE) { lwerror("Bad format, seconds (SS.SSS) number of digits was greater than our working limit."); } snprintf(pieces[sec_piece], WORK_SIZE, "%*.*f", sec_digits, sec_dec_digits, seconds); } /* Allocate space for the result. Leave plenty of room for excess digits, negative sign, etc.*/ result = (char*)lwalloc(format_length + WORK_SIZE); memset(result, 0, format_length + WORK_SIZE); /* Append all the pieces together. There may be less than 9, but in that case the rest will be blank. */ strcpy(result, pieces[0]); for (index = 1; index < NUM_PIECES; index++) { strcat(result, pieces[index]); } return result; } /* Print two doubles (lat and lon) in DMS form using the specified format. * First normalizes them so they will display as -90 to 90 and -180 to 180. * Format string may be null or 0-length, in which case a default format will be used. * NOTE: Format string is required to be in UTF-8. * NOTE2: returned string is lwalloc'ed, caller is responsible to lwfree it up */ static char * lwdoubles_to_latlon(double lat, double lon, const char * format) { char * lat_text; char * lon_text; char * result; /* Normalize lat/lon to the normal (-90 to 90, -180 to 180) range. */ lwprint_normalize_latlon(&lat, &lon); /* This is somewhat inefficient as the format is parsed twice. */ lat_text = lwdouble_to_dms(lat, "N", "S", format); lon_text = lwdouble_to_dms(lon, "E", "W", format); /* lat + lon + a space between + the null terminator. */ result = (char*)lwalloc(strlen(lat_text) + strlen(lon_text) + 2); snprintf(result, strlen(lat_text) + strlen(lon_text) + 2, "%s %s", lat_text, lon_text); lwfree(lat_text); lwfree(lon_text); return result; } /* Print the X (lon) and Y (lat) of the given point in DMS form using * the specified format. * First normalizes the values so they will display as -90 to 90 and -180 to 180. * Format string may be null or 0-length, in which case a default format will be used. * NOTE: Format string is required to be in UTF-8. * NOTE2: returned string is lwalloc'ed, caller is responsible to lwfree it up */ char* lwpoint_to_latlon(const LWPOINT * pt, const char *format) { const POINT2D *p; if (NULL == pt) { lwerror("Cannot convert a null point into formatted text."); } if (lwgeom_is_empty((LWGEOM *)pt)) { lwerror("Cannot convert an empty point into formatted text."); } p = getPoint2d_cp(pt->point, 0); return lwdoubles_to_latlon(p->y, p->x, format); } /* * Removes trailing zeros and dot for a %f formatted number. * Modifies input. */ static void trim_trailing_zeros(char* str) { char *ptr, *totrim = NULL; int len; int i; LWDEBUGF(3, "input: %s", str); ptr = strchr(str, '.'); if (!ptr) return; /* no dot, no decimal digits */ LWDEBUGF(3, "ptr: %s", ptr); len = strlen(ptr); for (i = len - 1; i; i--) { if (ptr[i] != '0') break; totrim = &ptr[i]; } if (totrim) { if (ptr == totrim - 1) *ptr = '\0'; else *totrim = '\0'; } LWDEBUGF(3, "output: %s", str); } /* * Print an ordinate value using at most the given number of decimal digits * * The actual number of printed decimal digits may be less than the * requested ones if out of significant digits. * * The function will not write more than maxsize bytes, including the * terminating NULL. Returns the number of bytes that would have been * written if there was enough space (excluding terminating NULL). * So a return of ``bufsize'' or more means that the string was * truncated and misses a terminating NULL. * */ int lwprint_double(double d, int maxdd, char* buf, size_t bufsize) { double ad = fabs(d); int ndd; int length = 0; if (ad <= FP_TOLERANCE) { d = 0; ad = 0; } if (ad < OUT_MAX_DOUBLE) { ndd = ad < 1 ? 0 : floor(log10(ad)) + 1; /* non-decimal digits */ if (maxdd > (OUT_MAX_DOUBLE_PRECISION - ndd)) maxdd -= ndd; length = snprintf(buf, bufsize, "%.*f", maxdd, d); } else { length = snprintf(buf, bufsize, "%g", d); } trim_trailing_zeros(buf); return length; } lwgeom/src/liblwgeom/lwgeom_geos_node.c0000644000176200001440000001402013773172540020002 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2011 Sandro Santilli * **********************************************************************/ #include "lwgeom_geos.h" #include "liblwgeom_internal.h" #include #include static int lwgeom_ngeoms(const LWGEOM* n) { const LWCOLLECTION* c = lwgeom_as_lwcollection(n); if ( c ) return c->ngeoms; else return 1; } static const LWGEOM* lwgeom_subgeom(const LWGEOM* g, int n) { const LWCOLLECTION* c = lwgeom_as_lwcollection(g); if ( c ) return lwcollection_getsubgeom((LWCOLLECTION*)c, n); else return g; } static void lwgeom_collect_endpoints(const LWGEOM* lwg, LWMPOINT* col) { int i, n; LWLINE* l; switch (lwg->type) { case MULTILINETYPE: for ( i = 0, n = lwgeom_ngeoms(lwg); i < n; ++i ) { lwgeom_collect_endpoints( lwgeom_subgeom(lwg, i), col); } break; case LINETYPE: l = (LWLINE*)lwg; col = lwmpoint_add_lwpoint(col, lwline_get_lwpoint(l, 0)); col = lwmpoint_add_lwpoint(col, lwline_get_lwpoint(l, l->points->npoints-1)); break; default: lwerror("lwgeom_collect_endpoints: invalid type %s", lwtype_name(lwg->type)); break; } } static LWMPOINT* lwgeom_extract_endpoints(const LWGEOM* lwg) { LWMPOINT* col = lwmpoint_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(lwg->flags), FLAGS_GET_M(lwg->flags)); lwgeom_collect_endpoints(lwg, col); return col; } /* Assumes initGEOS was called already */ /* May return LWPOINT or LWMPOINT */ static LWGEOM* lwgeom_extract_unique_endpoints(const LWGEOM* lwg) { LWGEOM* ret; GEOSGeometry *gepu; LWMPOINT *epall = lwgeom_extract_endpoints(lwg); GEOSGeometry *gepall = LWGEOM2GEOS((LWGEOM*)epall, 1); lwmpoint_free(epall); if ( ! gepall ) { lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } /* UnaryUnion to remove duplicates */ /* TODO: do it all within pgis using indices */ gepu = GEOSUnaryUnion(gepall); if ( ! gepu ) { GEOSGeom_destroy(gepall); lwerror("GEOSUnaryUnion: %s", lwgeom_geos_errmsg); return NULL; } GEOSGeom_destroy(gepall); ret = GEOS2LWGEOM(gepu, FLAGS_GET_Z(lwg->flags)); GEOSGeom_destroy(gepu); if ( ! ret ) { lwerror("Error during GEOS2LWGEOM"); return NULL; } return ret; } /* exported */ extern LWGEOM* lwgeom_node(const LWGEOM* lwgeom_in); LWGEOM* lwgeom_node(const LWGEOM* lwgeom_in) { GEOSGeometry *g1, *gn, *gm; LWGEOM *ep, *lines; LWCOLLECTION *col, *tc; int pn, ln, np, nl; if ( lwgeom_dimension(lwgeom_in) != 1 ) { lwerror("Noding geometries of dimension != 1 is unsupported"); return NULL; } initGEOS(lwgeom_geos_error, lwgeom_geos_error); g1 = LWGEOM2GEOS(lwgeom_in, 1); if ( ! g1 ) { lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } ep = lwgeom_extract_unique_endpoints(lwgeom_in); if ( ! ep ) { GEOSGeom_destroy(g1); lwerror("Error extracting unique endpoints from input"); return NULL; } gn = GEOSNode(g1); GEOSGeom_destroy(g1); if ( ! gn ) { lwgeom_free(ep); lwerror("GEOSNode: %s", lwgeom_geos_errmsg); return NULL; } gm = GEOSLineMerge(gn); GEOSGeom_destroy(gn); if ( ! gm ) { lwgeom_free(ep); lwerror("GEOSLineMerge: %s", lwgeom_geos_errmsg); return NULL; } lines = GEOS2LWGEOM(gm, FLAGS_GET_Z(lwgeom_in->flags)); GEOSGeom_destroy(gm); if ( ! lines ) { lwgeom_free(ep); lwerror("Error during GEOS2LWGEOM"); return NULL; } /* * Reintroduce endpoints from input, using split-line-by-point. * Note that by now we can be sure that each point splits at * most _one_ segment as any point shared by multiple segments * would already be a node. Also we can be sure that any of * the segments endpoints won't split any other segment. * We can use the above 2 assertions to early exit the loop. */ col = lwcollection_construct_empty(MULTILINETYPE, lwgeom_in->srid, FLAGS_GET_Z(lwgeom_in->flags), FLAGS_GET_M(lwgeom_in->flags)); np = lwgeom_ngeoms(ep); for (pn=0; pn ln+1) { tc->geoms[nl] = tc->geoms[nl-1]; --nl; } lwgeom_free(tc->geoms[ln]); tc->geoms[ln] = col->geoms[0]; tc->geoms[ln+1] = col->geoms[1]; tc->ngeoms++; } else { lwgeom_free(lines); /* transfer ownership rather than cloning */ lines = (LWGEOM*)lwcollection_clone_deep(col); assert(col->ngeoms == 2); lwgeom_free(col->geoms[0]); lwgeom_free(col->geoms[1]); } /* reset the vector */ assert(col->ngeoms == 2); col->ngeoms = 0; break; } } lwgeom_free(ep); lwcollection_free(col); lwgeom_set_srid(lines, lwgeom_in->srid); return (LWGEOM*)lines; } lwgeom/src/liblwgeom/liblwgeom.h0000644000176200001440000024020714332732612016456 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2011 Paul Ramsey * Copyright 2007-2008 Mark Cave-Ayland * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef _LIBLWGEOM_H #define _LIBLWGEOM_H 1 #include #include #include #include "../postgis_config.h" #if POSTGIS_PROJ_VERSION < 60 #include "proj_api.h" typedef struct PJ { projPJ pj_from; projPJ pj_to; } PJ; typedef PJ LWPROJ; #else #include "proj.h" /* For PROJ6 we cache several extra values to avoid calls to proj_get_source_crs * or proj_get_target_crs since those are very costly */ typedef struct LWPROJ { PJ* pj; /* CRSs are swapped: Used in transformation calls */ uint8_t source_swapped; uint8_t target_swapped; /* Source crs is geographic: Used in geography calls (source srid == dst srid) */ uint8_t source_is_latlong; /* Source ellipsoid parameters */ double source_semi_major_metre; double source_semi_minor_metre; } LWPROJ; #endif #if POSTGIS_PROJ_VERSION < 49 /* Use the old (pre-2.2) geodesic functions */ #undef PROJ_GEODESIC #else /* Enable new geodesic functions API */ #define PROJ_GEODESIC #endif /** * @file liblwgeom.h * * This library is the generic geometry handling section of PostGIS. The geometry * objects, constructors, destructors, and a set of spatial processing functions, * are implemented here. * * The library is designed for use in non-PostGIS applications if necessary. The * units tests at cunit/cu_tester.c and the loader/dumper programs at * ../loader/shp2pgsql.c are examples of non-PostGIS applications using liblwgeom. * * Programs using this library can install their custom memory managers and error * handlers by calling the lwgeom_set_handlers() function, otherwise the default * ones will be used. */ /** * liblwgeom versions */ #define LIBLWGEOM_VERSION "3.0.0beta1" #define LIBLWGEOM_VERSION_MAJOR "3" #define LIBLWGEOM_VERSION_MINOR "0" #define LIBLWGEOM_GEOS_VERSION "37" /** Return lwgeom version string (not to be freed) */ const char* lwgeom_version(void); /** * Return types for functions with status returns. */ #define LW_TRUE 1 #define LW_FALSE 0 #define LW_UNKNOWN 2 #define LW_FAILURE 0 #define LW_SUCCESS 1 /** * LWTYPE numbers, used internally by PostGIS */ #define POINTTYPE 1 #define LINETYPE 2 #define POLYGONTYPE 3 #define MULTIPOINTTYPE 4 #define MULTILINETYPE 5 #define MULTIPOLYGONTYPE 6 #define COLLECTIONTYPE 7 #define CIRCSTRINGTYPE 8 #define COMPOUNDTYPE 9 #define CURVEPOLYTYPE 10 #define MULTICURVETYPE 11 #define MULTISURFACETYPE 12 #define POLYHEDRALSURFACETYPE 13 #define TRIANGLETYPE 14 #define TINTYPE 15 #define NUMTYPES 16 /** * Flags applied in EWKB to indicate Z/M dimensions and * presence/absence of SRID and bounding boxes */ #define WKBZOFFSET 0x80000000 #define WKBMOFFSET 0x40000000 #define WKBSRIDFLAG 0x20000000 #define WKBBBOXFLAG 0x10000000 /** Ordinate names */ typedef enum LWORD_T { LWORD_X = 0, LWORD_Y = 1, LWORD_Z = 2, LWORD_M = 3 } LWORD; /********************************************************************** ** Spherical radius. ** Moritz, H. (1980). Geodetic Reference System 1980, by resolution of ** the XVII General Assembly of the IUGG in Canberra. ** http://en.wikipedia.org/wiki/Earth_radius ** http://en.wikipedia.org/wiki/World_Geodetic_System */ #define WGS84_MAJOR_AXIS 6378137.0 #define WGS84_INVERSE_FLATTENING 298.257223563 #define WGS84_MINOR_AXIS (WGS84_MAJOR_AXIS - WGS84_MAJOR_AXIS / WGS84_INVERSE_FLATTENING) #define WGS84_RADIUS ((2.0 * WGS84_MAJOR_AXIS + WGS84_MINOR_AXIS ) / 3.0) #define WGS84_SRID 4326 /** * Macros for manipulating the 'flags' byte. A uint8_t used as follows: * VVSRGBMZ * Version bit, followed by * Validty, Solid, ReadOnly, Geodetic, HasBBox, HasM and HasZ flags. */ #define LWFLAG_Z 0x01 #define LWFLAG_M 0x02 #define LWFLAG_BBOX 0x04 #define LWFLAG_GEODETIC 0x08 #define LWFLAG_READONLY 0x10 #define LWFLAG_SOLID 0x20 #define FLAGS_GET_Z(flags) ((flags) & LWFLAG_Z) #define FLAGS_GET_M(flags) (((flags) & LWFLAG_M)>>1) #define FLAGS_GET_BBOX(flags) (((flags) & LWFLAG_BBOX)>>2) #define FLAGS_GET_GEODETIC(flags) (((flags) & LWFLAG_GEODETIC)>>3) #define FLAGS_GET_READONLY(flags) (((flags) & LWFLAG_READONLY)>>4) #define FLAGS_GET_SOLID(flags) (((flags) & LWFLAG_SOLID)>>5) #define FLAGS_SET_Z(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_Z) : ((flags) & ~LWFLAG_Z)) #define FLAGS_SET_M(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_M) : ((flags) & ~LWFLAG_M)) #define FLAGS_SET_BBOX(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_BBOX) : ((flags) & ~LWFLAG_BBOX)) #define FLAGS_SET_GEODETIC(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_GEODETIC) : ((flags) & ~LWFLAG_GEODETIC)) #define FLAGS_SET_READONLY(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_READONLY) : ((flags) & ~LWFLAG_READONLY)) #define FLAGS_SET_SOLID(flags, value) ((flags) = (value) ? ((flags) | LWFLAG_SOLID) : ((flags) & ~LWFLAG_SOLID)) #define FLAGS_NDIMS(flags) (2 + FLAGS_GET_Z(flags) + FLAGS_GET_M(flags)) #define FLAGS_GET_ZM(flags) (FLAGS_GET_M(flags) + FLAGS_GET_Z(flags) * 2) #define FLAGS_NDIMS_BOX(flags) (FLAGS_GET_GEODETIC(flags) ? 3 : FLAGS_NDIMS(flags)) /** * Macros for manipulating the 'typemod' int. An int32_t used as follows: * Plus/minus = Top bit. * Spare bits = Next 2 bits. * SRID = Next 21 bits. * TYPE = Next 6 bits. * ZM Flags = Bottom 2 bits. */ #define TYPMOD_GET_SRID(typmod) ((((typmod) & 0x0FFFFF00) - ((typmod) & 0x10000000)) >> 8) #define TYPMOD_SET_SRID(typmod, srid) ((typmod) = (((typmod) & 0xE00000FF) | ((srid & 0x001FFFFF)<<8))) #define TYPMOD_GET_TYPE(typmod) ((typmod & 0x000000FC)>>2) #define TYPMOD_SET_TYPE(typmod, type) ((typmod) = (typmod & 0xFFFFFF03) | ((type & 0x0000003F)<<2)) #define TYPMOD_GET_Z(typmod) ((typmod & 0x00000002)>>1) #define TYPMOD_SET_Z(typmod) ((typmod) = typmod | 0x00000002) #define TYPMOD_GET_M(typmod) (typmod & 0x00000001) #define TYPMOD_SET_M(typmod) ((typmod) = typmod | 0x00000001) #define TYPMOD_GET_NDIMS(typmod) (2+TYPMOD_GET_Z(typmod)+TYPMOD_GET_M(typmod)) /** * Maximum allowed SRID value in serialized geometry. * Currently we are using 21 bits (2097152) of storage for SRID. */ #define SRID_MAXIMUM 999999 /** * Maximum valid SRID value for the user * We reserve 1000 values for internal use */ #define SRID_USER_MAXIMUM 998999 /** Unknown SRID value */ #define SRID_UNKNOWN 0 #define SRID_IS_UNKNOWN(x) ((int)x<=0) /* Invalid SRID value, for internal use */ #define SRID_INVALID (999999 + 2) /* ** EPSG WGS84 geographics, OGC standard default SRS, better be in ** the SPATIAL_REF_SYS table! */ #define SRID_DEFAULT 4326 #ifndef __GNUC__ # define __attribute__(x) #endif /** * Return a valid SRID from an arbitrary integer * Raises a notice if what comes out is different from * what went in. * Raises an error if SRID value is out of bounds. */ extern int32_t clamp_srid(int32_t srid); /** * Global functions for memory/logging handlers. */ typedef void* (*lwallocator)(size_t size); typedef void* (*lwreallocator)(void *mem, size_t size); typedef void (*lwfreeor)(void* mem); typedef void (*lwreporter)(const char* fmt, va_list ap) __attribute__ (( format(printf, 1, 0) )); typedef void (*lwdebuglogger)(int level, const char* fmt, va_list ap) __attribute__ (( format(printf, 2,0) )); /** * Install custom memory management and error handling functions you want your * application to use. * @ingroup system * @todo take a structure ? */ extern void lwgeom_set_handlers(lwallocator allocator, lwreallocator reallocator, lwfreeor freeor, lwreporter errorreporter, lwreporter noticereporter); extern void lwgeom_set_debuglogger(lwdebuglogger debuglogger); /** * Request interruption of any running code * * Safe for use from signal handlers * * Interrupted code will (as soon as it finds out * to be interrupted) cleanup and return as soon as possible. * * The return value from interrupted code is undefined, * it is the caller responsibility to not take it in consideration. * */ extern void lwgeom_request_interrupt(void); /** * Cancel any interruption request */ extern void lwgeom_cancel_interrupt(void); /** * Install a callback to be called periodically during * algorithm execution. Mostly only needed on WIN32 to * dispatch queued signals. * * The callback is invoked before checking for interrupt * being requested, so you can request interruption from * the callback, if you want (see lwgeom_request_interrupt). * */ typedef void (lwinterrupt_callback)(void); extern lwinterrupt_callback *lwgeom_register_interrupt_callback(lwinterrupt_callback *); /****************************************************************** * LWGEOM and GBOX both use LWFLAGS bit mask. * Serializations (may) use different bit mask schemes. */ typedef uint16_t lwflags_t; /******************************************************************/ typedef struct { double afac, bfac, cfac, dfac, efac, ffac, gfac, hfac, ifac, xoff, yoff, zoff; } AFFINE; /******************************************************************/ typedef struct { double xmin, ymin, zmin; double xmax, ymax, zmax; int32_t srid; } BOX3D; /****************************************************************** * GBOX structure. * We include the flags (information about dimensionality), * so we don't have to constantly pass them * into functions that use the GBOX. */ typedef struct { lwflags_t flags; double xmin; double xmax; double ymin; double ymax; double zmin; double zmax; double mmin; double mmax; } GBOX; /****************************************************************** * SPHEROID * * Standard definition of an ellipsoid (what wkt calls a spheroid) * f = (a-b)/a * e_sq = (a*a - b*b)/(a*a) * b = a - fa */ typedef struct { double a; /* semimajor axis */ double b; /* semiminor axis b = (a - fa) */ double f; /* flattening f = (a-b)/a */ double e; /* eccentricity (first) */ double e_sq; /* eccentricity squared (first) e_sq = (a*a-b*b)/(a*a) */ double radius; /* spherical average radius = (2*a+b)/3 */ char name[20]; /* name of ellipse */ } SPHEROID; /****************************************************************** * POINT2D, POINT3D, POINT3DM, POINT4D */ typedef struct { double x, y; } POINT2D; typedef struct { double x, y, z; } POINT3DZ; typedef struct { double x, y, z; } POINT3D; typedef struct { double x, y, m; } POINT3DM; typedef struct { double x, y, z, m; } POINT4D; /****************************************************************** * POINTARRAY * Point array abstracts a lot of the complexity of points and point lists. * It handles 2d/3d translation * (2d points converted to 3d will have z=0 or NaN) * DO NOT MIX 2D and 3D POINTS! EVERYTHING* is either one or the other */ typedef struct { uint32_t npoints; /* how many points we are currently storing */ uint32_t maxpoints; /* how many points we have space for in serialized_pointlist */ /* Use FLAGS_* macros to handle */ lwflags_t flags; /* Array of POINT 2D, 3D or 4D, possibly misaligned. */ uint8_t *serialized_pointlist; } POINTARRAY; /****************************************************************** * GSERIALIZED */ typedef struct { uint32_t size; /* For PgSQL use only, use VAR* macros to manipulate. */ uint8_t srid[3]; /* 24 bits of SRID */ uint8_t gflags; /* HasZ, HasM, HasBBox, IsGeodetic */ uint8_t data[1]; /* See gserialized.txt */ } GSERIALIZED; /****************************************************************** * LWGEOM (any geometry type) * * Abstract type, note that 'type', 'bbox' and 'srid' are available in * all geometry variants. */ typedef struct { GBOX *bbox; void *data; int32_t srid; lwflags_t flags; uint8_t type; char pad[1]; /* Padding to 24 bytes (unused) */ } LWGEOM; /* POINTYPE */ typedef struct { GBOX *bbox; POINTARRAY *point; /* hide 2d/3d (this will be an array of 1 point) */ int32_t srid; lwflags_t flags; uint8_t type; /* POINTTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ } LWPOINT; /* "light-weight point" */ /* LINETYPE */ typedef struct { GBOX *bbox; POINTARRAY *points; /* array of POINT3D */ int32_t srid; lwflags_t flags; uint8_t type; /* LINETYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ } LWLINE; /* "light-weight line" */ /* TRIANGLE */ typedef struct { GBOX *bbox; POINTARRAY *points; int32_t srid; lwflags_t flags; uint8_t type; char pad[1]; /* Padding to 24 bytes (unused) */ } LWTRIANGLE; /* CIRCSTRINGTYPE */ typedef struct { GBOX *bbox; POINTARRAY *points; /* array of POINT(3D/3DM) */ int32_t srid; lwflags_t flags; uint8_t type; /* CIRCSTRINGTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ } LWCIRCSTRING; /* "light-weight circularstring" */ /* POLYGONTYPE */ typedef struct { GBOX *bbox; POINTARRAY **rings; /* list of rings (list of points) */ int32_t srid; lwflags_t flags; uint8_t type; /* POLYGONTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t nrings; /* how many rings we are currently storing */ uint32_t maxrings; /* how many rings we have space for in **rings */ } LWPOLY; /* "light-weight polygon" */ /* MULTIPOINTTYPE */ typedef struct { GBOX *bbox; LWPOINT **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* MULTYPOINTTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWMPOINT; /* MULTILINETYPE */ typedef struct { GBOX *bbox; LWLINE **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* MULTILINETYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWMLINE; /* MULTIPOLYGONTYPE */ typedef struct { GBOX *bbox; LWPOLY **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* MULTIPOLYGONTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWMPOLY; /* COLLECTIONTYPE */ typedef struct { GBOX *bbox; LWGEOM **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* COLLECTIONTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWCOLLECTION; /* COMPOUNDTYPE */ typedef struct { GBOX *bbox; LWGEOM **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* COLLECTIONTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWCOMPOUND; /* "light-weight compound line" */ /* CURVEPOLYTYPE */ typedef struct { GBOX *bbox; LWGEOM **rings; int32_t srid; lwflags_t flags; uint8_t type; /* CURVEPOLYTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t nrings; /* how many rings we are currently storing */ uint32_t maxrings; /* how many rings we have space for in **rings */ } LWCURVEPOLY; /* "light-weight polygon" */ /* MULTICURVE */ typedef struct { GBOX *bbox; LWGEOM **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* MULTICURVE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWMCURVE; /* MULTISURFACETYPE */ typedef struct { GBOX *bbox; LWGEOM **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* MULTISURFACETYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWMSURFACE; /* POLYHEDRALSURFACETYPE */ typedef struct { GBOX *bbox; LWPOLY **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* POLYHEDRALSURFACETYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWPSURFACE; /* TINTYPE */ typedef struct { GBOX *bbox; LWTRIANGLE **geoms; int32_t srid; lwflags_t flags; uint8_t type; /* TINTYPE */ char pad[1]; /* Padding to 24 bytes (unused) */ uint32_t ngeoms; /* how many geometries we are currently storing */ uint32_t maxgeoms; /* how many geometries we have space for in **geoms */ } LWTIN; /* Casts LWGEOM->LW* (return NULL if cast is illegal) */ extern LWMPOLY *lwgeom_as_lwmpoly(const LWGEOM *lwgeom); extern LWMLINE *lwgeom_as_lwmline(const LWGEOM *lwgeom); extern LWMPOINT *lwgeom_as_lwmpoint(const LWGEOM *lwgeom); extern LWCOLLECTION *lwgeom_as_lwcollection(const LWGEOM *lwgeom); extern LWPOLY *lwgeom_as_lwpoly(const LWGEOM *lwgeom); extern LWLINE *lwgeom_as_lwline(const LWGEOM *lwgeom); extern LWCIRCSTRING *lwgeom_as_lwcircstring(const LWGEOM *lwgeom); extern LWCURVEPOLY *lwgeom_as_lwcurvepoly(const LWGEOM *lwgeom); extern LWCOMPOUND *lwgeom_as_lwcompound(const LWGEOM *lwgeom); extern LWPSURFACE *lwgeom_as_lwpsurface(const LWGEOM *lwgeom); extern LWTRIANGLE *lwgeom_as_lwtriangle(const LWGEOM *lwgeom); extern LWTIN *lwgeom_as_lwtin(const LWGEOM *lwgeom); extern LWGEOM *lwgeom_as_multi(const LWGEOM *lwgeom); extern LWGEOM *lwgeom_as_curve(const LWGEOM *lwgeom); /* Casts LW*->LWGEOM (always cast) */ extern LWGEOM *lwtin_as_lwgeom(const LWTIN *obj); extern LWGEOM *lwtriangle_as_lwgeom(const LWTRIANGLE *obj); extern LWGEOM *lwpsurface_as_lwgeom(const LWPSURFACE *obj); extern LWGEOM *lwmpoly_as_lwgeom(const LWMPOLY *obj); extern LWGEOM *lwmline_as_lwgeom(const LWMLINE *obj); extern LWGEOM *lwmpoint_as_lwgeom(const LWMPOINT *obj); extern LWGEOM *lwcollection_as_lwgeom(const LWCOLLECTION *obj); extern LWGEOM *lwcircstring_as_lwgeom(const LWCIRCSTRING *obj); extern LWGEOM *lwcompound_as_lwgeom(const LWCOMPOUND *obj); extern LWGEOM *lwcurvepoly_as_lwgeom(const LWCURVEPOLY *obj); extern LWGEOM *lwpoly_as_lwgeom(const LWPOLY *obj); extern LWGEOM *lwline_as_lwgeom(const LWLINE *obj); extern LWGEOM *lwpoint_as_lwgeom(const LWPOINT *obj); extern LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom); extern LWMPOINT* lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj); extern LWMLINE* lwmline_add_lwline(LWMLINE *mobj, const LWLINE *obj); extern LWMPOLY* lwmpoly_add_lwpoly(LWMPOLY *mobj, const LWPOLY *obj); extern LWPSURFACE* lwpsurface_add_lwpoly(LWPSURFACE *mobj, const LWPOLY *obj); extern LWTIN* lwtin_add_lwtriangle(LWTIN *mobj, const LWTRIANGLE *obj); extern LWCOLLECTION* lwcollection_concat_in_place(LWCOLLECTION* col1, const LWCOLLECTION* col2); /** * Construct a new flags bitmask. */ extern lwflags_t lwflags(int hasz, int hasm, int geodetic); /*********************************************************************** ** GSERIALIZED API */ /** * Read standard lwflags from gserialized */ extern lwflags_t gserialized_get_lwflags(const GSERIALIZED *g); /** * Access to the float bounding box, if there is one. * NULL if there is not. */ extern const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims); /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ extern uint32_t gserialized_get_type(const GSERIALIZED *g); /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ extern uint32_t gserialized_max_header_size(void); /** * Returns a hash code for the srid/type/geometry information * in the GSERIALIZED. Ignores metadata like flags and optional * boxes, etc. */ extern int32_t gserialized_hash(const GSERIALIZED *g); /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ extern int32_t gserialized_get_srid(const GSERIALIZED *g); /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ extern void gserialized_set_srid(GSERIALIZED *g, int32_t srid); /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ extern int gserialized_is_empty(const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ extern int gserialized_has_bbox(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has a Z ordinate. */ extern int gserialized_has_z(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an M ordinate. */ extern int gserialized_has_m(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED is a geography. */ extern int gserialized_is_geodetic(const GSERIALIZED *gser); /** * Return the number of dimensions (2, 3, 4) in a geometry */ extern int gserialized_ndims(const GSERIALIZED *gser); /** * Return -1 if g1 is "less than" g2, 1 if g1 is "greater than" * g2 and 0 if g1 and g2 are the "same". Equality is evaluated * with a memcmp and size check. So it is possible that two * identical objects where one lacks a bounding box could be * evaluated as non-equal initially. Greater and less than * are evaluated by calculating a sortable key from the center * point of the object bounds. */ extern int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2); /** * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ extern GSERIALIZED* gserialized_from_lwgeom(LWGEOM *geom, size_t *size); /** * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates * that are double aligned and suitable for direct reading using getPoint2d_p_ro */ extern LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g); /** * Pull a #GBOX from the header of a #GSERIALIZED, if one is available. If * it is not, calculate it from the geometry. If that doesn't work (null * or empty) return LW_FAILURE. */ extern int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box); /** * Pull a #GBOX from the header of a #GSERIALIZED, if one is available. If * it is not, return LW_FAILURE. */ extern int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *box); /** * Copy a new bounding box into an existing gserialized. * If necessary a new #GSERIALIZED will be allocated. Test * that input != output before freeing input. */ extern GSERIALIZED *gserialized_set_gbox(GSERIALIZED *g, GBOX *gbox); /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ extern GSERIALIZED* gserialized_drop_gbox(GSERIALIZED *g); /** * Return the serialization version */ extern uint32_t gserialized_get_version(const GSERIALIZED *g); /** * Pull the first point values of a #GSERIALIZED. Only works for POINTTYPE */ extern int gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point); /*****************************************************************************/ /** * Call this function to drop BBOX and SRID * from LWGEOM. If LWGEOM type is *not* flagged * with the HASBBOX flag and has a bbox, it * will be released. */ extern void lwgeom_drop_bbox(LWGEOM *lwgeom); extern void lwgeom_drop_srid(LWGEOM *lwgeom); /** * Compute a bbox if not already computed * * After calling this function lwgeom->bbox is only * NULL if the geometry is empty. */ extern void lwgeom_add_bbox(LWGEOM *lwgeom); /** * Drop current bbox and calculate a fresh one. */ extern void lwgeom_refresh_bbox(LWGEOM *lwgeom); /** * Compute a box for geom and all sub-geometries, if not already computed */ extern void lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox); /** * Get a non-empty geometry bounding box, computing and * caching it if not already there * * NOTE: empty geometries don't have a bounding box so * you'd still get a NULL for them. */ extern const GBOX *lwgeom_get_bbox(const LWGEOM *lwgeom); /** * Determine whether a LWGEOM can contain sub-geometries or not */ extern int lwgeom_is_collection(const LWGEOM *lwgeom); /******************************************************************/ /* Functions that work on type numbers */ /** * Determine whether a type number is a collection or not */ extern int lwtype_is_collection(uint8_t type); /** * Given an lwtype number, what homogeneous collection can hold it? */ extern uint32_t lwtype_get_collectiontype(uint8_t type); /** * Return the type name string associated with a type number * (e.g. Point, LineString, Polygon) */ extern const char *lwtype_name(uint8_t type); extern uint8_t lwtype_multitype(uint8_t type); /******************************************************************/ /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * will set point's m=0 (or NaN) if pa is 3d or 2d * NOTE: point is a real POINT3D *not* a pointer */ extern POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * will set point's m=0 (or NaN) if pa is 3d or 2d * NOTE: this will modify the point4d pointed to by 'point'. */ extern int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * NOTE: point is a real POINT3D *not* a pointer */ extern POINT3DZ getPoint3dz(const POINTARRAY *pa, uint32_t n); extern POINT3DM getPoint3dm(const POINTARRAY *pa, uint32_t n); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * NOTE: this will modify the point3d pointed to by 'point'. */ extern int getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *point); extern int getPoint3dm_p(const POINTARRAY *pa, uint32_t n, POINT3DM *point); /* * copies a point from the point array into the parameter point * z value (if present is not returned) * NOTE: point is a real POINT3D *not* a pointer */ extern POINT2D getPoint2d(const POINTARRAY *pa, uint32_t n); /* * copies a point from the point array into the parameter point * z value (if present is not returned) * NOTE: this will modify the point2d pointed to by 'point'. */ extern int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point); /* * set point N to the given value * NOTE that the pointarray can be of any * dimension, the appropriate ordinate values * will be extracted from it * * N must be a valid point index */ extern void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d); /** * Construct an empty pointarray, allocating storage and setting * the npoints, but not filling in any information. Should be used in conjunction * with ptarray_set_point4d to fill in the information in the array. */ extern POINTARRAY* ptarray_construct(char hasz, char hasm, uint32_t npoints); /** * Construct a new #POINTARRAY, copying in the data from ptlist */ extern POINTARRAY* ptarray_construct_copy_data(char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist); /** * Construct a new #POINTARRAY, referencing to the data from ptlist */ extern POINTARRAY* ptarray_construct_reference_data(char hasz, char hasm, uint32_t npoints, uint8_t *ptlist); /** * Create a new #POINTARRAY with no points. Allocate enough storage * to hold maxpoints vertices before having to reallocate the storage * area. */ extern POINTARRAY* ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints); /** * Append a point to the end of an existing #POINTARRAY * If allow_duplicate is LW_FALSE, then a duplicate point will * not be added. */ extern int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates); /** * Append a #POINTARRAY, pa2 to the end of an existing #POINTARRAY, pa1. * * If gap_tolerance is >= 0 then the end point of pa1 will be checked for * being within gap_tolerance 2d distance from start point of pa2 or an * error will be raised and LW_FAILURE returned. * A gap_tolerance < 0 disables the check. * * If end point of pa1 and start point of pa2 are 2d-equal, then pa2 first * point will not be appended. */ extern int ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance); /** * Insert a point into an existing #POINTARRAY. Zero * is the index of the start of the array. */ extern int ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where); /** * Remove a point from an existing #POINTARRAY. Zero * is the index of the start of the array. */ extern int ptarray_remove_point(POINTARRAY *pa, uint32_t where); /** * @brief Add a point in a pointarray. * * @param pa the source POINTARRAY * @param p the point to add * @param pdims number of ordinates in p (2..4) * @param where to insert the point. 0 prepends, pa->npoints appends * * @returns a newly constructed POINTARRAY using a newly allocated buffer * for the actual points, or NULL on error. */ extern POINTARRAY *ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where); /** * @brief Remove a point from a pointarray. * @param which - is the offset (starting at 0) * @return #POINTARRAY is newly allocated */ extern POINTARRAY *ptarray_removePoint(POINTARRAY *pa, uint32_t where); /** * @brief Merge two given POINTARRAY and returns a pointer * on the new aggregate one. * Warning: this function free the two inputs POINTARRAY * @return #POINTARRAY is newly allocated */ extern POINTARRAY *ptarray_merge(POINTARRAY *pa1, POINTARRAY *pa2); extern int ptarray_is_closed(const POINTARRAY *pa); extern int ptarray_is_closed_2d(const POINTARRAY *pa); extern int ptarray_is_closed_3d(const POINTARRAY *pa); extern int ptarray_is_closed_z(const POINTARRAY *pa); extern POINTARRAY* ptarray_flip_coordinates(POINTARRAY *pa); /** * @d1 start location (distance from start / total distance) * @d2 end location (distance from start / total distance) * @param tolerance snap to vertices at locations < tolerance away from given ones */ extern POINTARRAY *ptarray_substring(POINTARRAY *pa, double d1, double d2, double tolerance); /** * Strip out the Z/M components of an #LWGEOM */ extern LWGEOM* lwgeom_force_2d(const LWGEOM *geom); extern LWGEOM* lwgeom_force_3dz(const LWGEOM *geom); extern LWGEOM* lwgeom_force_3dm(const LWGEOM *geom); extern LWGEOM* lwgeom_force_4d(const LWGEOM *geom); extern LWGEOM* lwgeom_set_effective_area(const LWGEOM *igeom, int set_area, double area); extern LWGEOM* lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint); extern LWGEOM* lwgeom_filter_m(LWGEOM *geom, double min, double max, int returnm); /* * Force to use SFS 1.1 geometry type * (rather than SFS 1.2 and/or SQL/MM) */ extern LWGEOM* lwgeom_force_sfs(LWGEOM *geom, int version); /*-------------------------------------------------------- * all the base types (point/line/polygon) will have a * basic constructor, basic de-serializer, basic serializer, * bounding box finder and (TODO) serialized form size finder. *--------------------------------------------------------*/ /* * convenience functions to hide the POINTARRAY */ extern int lwpoint_getPoint2d_p(const LWPOINT *point, POINT2D *out); extern int lwpoint_getPoint3dz_p(const LWPOINT *point, POINT3DZ *out); extern int lwpoint_getPoint3dm_p(const LWPOINT *point, POINT3DM *out); extern int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out); /****************************************************************** * LWLINE functions ******************************************************************/ /** * Add a LWPOINT to an LWLINE */ extern int lwline_add_lwpoint(LWLINE *line, LWPOINT *point, uint32_t where); /** * Interpolate one or more points along a line */ extern POINTARRAY* lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat); /** * Interpolate one point along a line in 3D */ extern LWPOINT* lwline_interpolate_point_3d(const LWLINE *line, double distance); /****************************************************************** * LWPOLY functions ******************************************************************/ /** * Add a ring, allocating extra space if necessary. The polygon takes * ownership of the passed point array. */ extern int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa); /** * Add a ring, allocating extra space if necessary. The curvepolygon takes * ownership of the passed point array. */ extern int lwcurvepoly_add_ring(LWCURVEPOLY *poly, LWGEOM *ring); /** * Add a component, allocating extra space if necessary. The compoundcurve * takes owership of the passed geometry. */ extern int lwcompound_add_lwgeom(LWCOMPOUND *comp, LWGEOM *geom); /** * Construct an equivalent compound curve from a linestring. * Compound curves can have linear components, so this works fine */ extern LWCOMPOUND* lwcompound_construct_from_lwline(const LWLINE *lwpoly); /** * Construct an equivalent curve polygon from a polygon. Curve polygons * can have linear rings as their rings, so this works fine (in theory?) */ extern LWCURVEPOLY* lwcurvepoly_construct_from_lwpoly(LWPOLY *lwpoly); /****************************************************************** * LWGEOM functions ******************************************************************/ extern int lwcollection_ngeoms(const LWCOLLECTION *col); /* Given a generic geometry/collection, return the "simplest" form. * The elements of the homogenized collection are references to the * input geometry; a deep clone is not performed. * TODO: consider returning a geometry that does not reference the * input * */ extern LWGEOM *lwgeom_homogenize(const LWGEOM *geom); /****************************************************************** * LWMULTIx and LWCOLLECTION functions ******************************************************************/ LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *col, int gnum); /* WARNING: the output will contain references to geometries in the input, */ /* so the result must be carefully released, not freed. */ LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type); /****************************************************************** * SERIALIZED FORM functions ******************************************************************/ /** * Set the SRID on an LWGEOM * For collections, only the parent gets an SRID, all * the children get SRID_UNKNOWN. */ extern void lwgeom_set_srid(LWGEOM *geom, int32_t srid); /*------------------------------------------------------ * other stuff * * handle the double-to-float conversion. The results of this * will usually be a slightly bigger box because of the difference * between float8 and float4 representations. */ extern BOX3D* box3d_from_gbox(const GBOX *gbox); extern GBOX* box3d_to_gbox(const BOX3D *b3d); void expand_box3d(BOX3D *box, double d); /**************************************************************** * MEMORY MANAGEMENT ****************************************************************/ /* * The *_free family of functions frees *all* memory associated * with the pointer. When the recursion gets to the level of the * POINTARRAY, the POINTARRAY is only freed if it is not flagged * as "read only". LWGEOMs constructed on top of GSERIALIZED * from PgSQL use read only point arrays. */ extern void ptarray_free(POINTARRAY *pa); extern void lwpoint_free(LWPOINT *pt); extern void lwline_free(LWLINE *line); extern void lwpoly_free(LWPOLY *poly); extern void lwtriangle_free(LWTRIANGLE *triangle); extern void lwmpoint_free(LWMPOINT *mpt); extern void lwmline_free(LWMLINE *mline); extern void lwmpoly_free(LWMPOLY *mpoly); extern void lwpsurface_free(LWPSURFACE *psurf); extern void lwtin_free(LWTIN *tin); extern void lwcollection_free(LWCOLLECTION *col); extern void lwcircstring_free(LWCIRCSTRING *curve); extern void lwgeom_free(LWGEOM *geom); /* * The *_release family of functions frees the LWGEOM structures * surrounding the POINTARRAYs but leaves the POINTARRAYs * intact. Useful when re-shaping geometries between types, * or splicing geometries together. */ extern void lwpoint_release(LWPOINT *lwpoint); extern void lwline_release(LWLINE *lwline); extern void lwpoly_release(LWPOLY *lwpoly); extern void lwtriangle_release(LWTRIANGLE *lwtriangle); extern void lwcircstring_release(LWCIRCSTRING *lwcirc); extern void lwmpoint_release(LWMPOINT *lwpoint); extern void lwmline_release(LWMLINE *lwline); extern void lwmpoly_release(LWMPOLY *lwpoly); extern void lwpsurface_release(LWPSURFACE *lwpsurface); extern void lwtin_release(LWTIN *lwtin); extern void lwcollection_release(LWCOLLECTION *lwcollection); extern void lwgeom_release(LWGEOM *lwgeom); /**************************************************************** * Utility ****************************************************************/ extern void printBOX3D(BOX3D *b); extern void printPA(POINTARRAY *pa); extern void printLWPOINT(LWPOINT *point); extern void printLWLINE(LWLINE *line); extern void printLWPOLY(LWPOLY *poly); extern void printLWTRIANGLE(LWTRIANGLE *triangle); extern void printLWPSURFACE(LWPSURFACE *psurf); extern void printLWTIN(LWTIN *tin); extern float next_float_down(double d); extern float next_float_up(double d); /* general utilities 2D */ extern double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2); extern double distance2d_sqr_pt_seg(const POINT2D *p, const POINT2D *A, const POINT2D *B); extern LWGEOM* lwgeom_closest_line(const LWGEOM *lw1, const LWGEOM *lw2); extern LWGEOM* lwgeom_furthest_line(const LWGEOM *lw1, const LWGEOM *lw2); extern LWGEOM* lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2); extern LWGEOM* lwgeom_furthest_point(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance); extern double lwgeom_maxdistance2d(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_maxdistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance); /* 3D */ extern double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2); extern double distance3d_pt_seg(const POINT3D *p, const POINT3D *A, const POINT3D *B); extern LWGEOM* lwgeom_furthest_line_3d(LWGEOM *lw1, LWGEOM *lw2); extern LWGEOM* lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2); extern LWGEOM* lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance); extern double lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2); extern double lwgeom_maxdistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance); extern double lwgeom_area(const LWGEOM *geom); extern double lwgeom_length(const LWGEOM *geom); extern double lwgeom_length_2d(const LWGEOM *geom); extern double lwgeom_perimeter(const LWGEOM *geom); extern double lwgeom_perimeter_2d(const LWGEOM *geom); extern int lwgeom_dimension(const LWGEOM *geom); extern LWPOINT* lwline_get_lwpoint(const LWLINE *line, uint32_t where); extern LWPOINT* lwcircstring_get_lwpoint(const LWCIRCSTRING *circ, uint32_t where); extern LWPOINT* lwcompound_get_startpoint(const LWCOMPOUND *lwcmp); extern LWPOINT* lwcompound_get_endpoint(const LWCOMPOUND *lwcmp); extern LWPOINT* lwcompound_get_lwpoint(const LWCOMPOUND *lwcmp, uint32_t where); extern double ptarray_length_2d(const POINTARRAY *pts); extern int pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring); extern int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret); extern int lwpoint_inside_circle(const LWPOINT *p, double cx, double cy, double rad); extern LWGEOM* lwgeom_reverse(const LWGEOM *lwgeom); extern char* lwgeom_summary(const LWGEOM *lwgeom, int offset); extern char* lwpoint_to_latlon(const LWPOINT *p, const char *format); extern int lwgeom_startpoint(const LWGEOM* lwgeom, POINT4D* pt); extern void interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F); /** * Ensure the outer ring is clockwise oriented and all inner rings * are counter-clockwise. */ extern int lwgeom_is_clockwise(LWGEOM *lwgeom); /** * Simplification */ extern LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed); extern LWGEOM* lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance); /** * Snap-to-grid */ typedef struct gridspec_t { double ipx; double ipy; double ipz; double ipm; double xsize; double ysize; double zsize; double msize; } gridspec; extern LWGEOM* lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid); extern void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid); /**************************************************************** * READ/WRITE FUNCTIONS * * Coordinate writing functions, which will alter the coordinates * and potentially the structure of the input geometry. When * called from within PostGIS, the LWGEOM argument should be built * on top of a gserialized copy, created using * PG_GETARG_GSERIALIZED_P_COPY() ****************************************************************/ extern void lwgeom_reverse_in_place(LWGEOM *lwgeom); extern void lwgeom_force_clockwise(LWGEOM *lwgeom); extern void lwgeom_longitude_shift(LWGEOM *lwgeom); extern int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed); extern void lwgeom_affine(LWGEOM *geom, const AFFINE *affine); extern void lwgeom_scale(LWGEOM *geom, const POINT4D *factors); extern int lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance); /** * @brief wrap geometry on given cut x value * * For a positive amount, shifts anything that is on the left * of "cutx" to the right by the given amount. * * For a negative amount, shifts anything that is on the right * of "cutx" to the left by the given absolute amount. * * @param cutx the X value to perform wrapping on * @param amount shift amount and wrapping direction */ LWGEOM *lwgeom_wrapx(const LWGEOM *lwgeom, double cutx, double amount); /** * @brief Check whether or not a lwgeom is big enough to warrant a bounding box. * * Check whether or not a lwgeom is big enough to warrant a bounding box * when stored in the serialized form on disk. Currently only points are * considered small enough to not require a bounding box, because the * index operations can generate a large number of box-retrieval operations * when scanning keys. */ extern int lwgeom_needs_bbox(const LWGEOM *geom); /** * Count the total number of vertices in any #LWGEOM. */ extern uint32_t lwgeom_count_vertices(const LWGEOM *geom); /** * Count the total number of rings in any #LWGEOM. Multipolygons * and other collections get counted, not the same as OGC st_numrings. */ extern uint32_t lwgeom_count_rings(const LWGEOM *geom); /** * Return true or false depending on whether a geometry has * a valid SRID set. */ extern int lwgeom_has_srid(const LWGEOM *geom); /** * Return true or false depending on whether a geometry is a linear * feature that closes on itself. */ extern int lwgeom_is_closed(const LWGEOM *geom); /** * Return the dimensionality (relating to point/line/poly) of an lwgeom */ extern int lwgeom_dimensionality(const LWGEOM *geom); /* Is lwgeom1 geometrically equal to lwgeom2 ? */ extern char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2); /** * @brief Clone LWGEOM object. Serialized point lists are not copied. * * #GBOX are copied * * @see ptarray_clone */ extern LWGEOM *lwgeom_clone(const LWGEOM *lwgeom); /** * Deep clone an LWGEOM, everything is copied */ extern LWGEOM *lwgeom_clone_deep(const LWGEOM *lwgeom); extern POINTARRAY *ptarray_clone_deep(const POINTARRAY *ptarray); /* * Geometry constructors. These constructors to not copy the point arrays * passed to them, they just take references, so do not free them out * from underneath the geometries. */ extern LWPOINT* lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point); extern LWMPOINT *lwmpoint_construct(int32_t srid, const POINTARRAY *pa); extern LWLINE* lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points); extern LWCIRCSTRING* lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points); extern LWPOLY* lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points); extern LWCURVEPOLY* lwcurvepoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, LWGEOM **geoms); extern LWTRIANGLE* lwtriangle_construct(int32_t srid, GBOX *bbox, POINTARRAY *points); extern LWCOLLECTION* lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms); /* * Empty geometry constructors. */ extern LWGEOM* lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm); extern LWPOINT* lwpoint_construct_empty(int32_t srid, char hasz, char hasm); extern LWLINE* lwline_construct_empty(int32_t srid, char hasz, char hasm); extern LWPOLY* lwpoly_construct_empty(int32_t srid, char hasz, char hasm); extern LWCURVEPOLY* lwcurvepoly_construct_empty(int32_t srid, char hasz, char hasm); extern LWCIRCSTRING* lwcircstring_construct_empty(int32_t srid, char hasz, char hasm); extern LWCOMPOUND* lwcompound_construct_empty(int32_t srid, char hasz, char hasm); extern LWTRIANGLE* lwtriangle_construct_empty(int32_t srid, char hasz, char hasm); extern LWMPOINT* lwmpoint_construct_empty(int32_t srid, char hasz, char hasm); extern LWMLINE* lwmline_construct_empty(int32_t srid, char hasz, char hasm); extern LWMPOLY* lwmpoly_construct_empty(int32_t srid, char hasz, char hasm); extern LWCOLLECTION* lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm); /* Other constructors */ extern LWPOINT *lwpoint_make2d(int32_t srid, double x, double y); extern LWPOINT *lwpoint_make3dz(int32_t srid, double x, double y, double z); extern LWPOINT *lwpoint_make3dm(int32_t srid, double x, double y, double m); extern LWPOINT *lwpoint_make4d(int32_t srid, double x, double y, double z, double m); extern LWPOINT *lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p); extern LWLINE *lwline_from_lwgeom_array(int32_t srid, uint32_t ngeoms, LWGEOM **geoms); extern LWLINE *lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points); /* TODO: deprecate */ extern LWLINE *lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint); extern LWLINE *lwline_addpoint(LWLINE *line, LWPOINT *point, uint32_t where); extern LWLINE *lwline_removepoint(LWLINE *line, uint32_t which); extern void lwline_setPoint4d(LWLINE *line, uint32_t which, POINT4D *newpoint); extern LWPOLY *lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes); extern LWPOLY *lwpoly_construct_rectangle(char hasz, char hasm, POINT4D *p1, POINT4D *p2, POINT4D *p3, POINT4D *p4); extern LWPOLY *lwpoly_construct_envelope(int32_t srid, double x1, double y1, double x2, double y2); extern LWPOLY *lwpoly_construct_circle(int32_t srid, double x, double y, double radius, uint32_t segments_per_quarter, char exterior); extern LWTRIANGLE *lwtriangle_from_lwline(const LWLINE *shell); extern LWMPOINT *lwmpoint_from_lwgeom(const LWGEOM *g); /* Extract the coordinates of an LWGEOM into an LWMPOINT */ /* Some point accessors */ extern double lwpoint_get_x(const LWPOINT *point); extern double lwpoint_get_y(const LWPOINT *point); extern double lwpoint_get_z(const LWPOINT *point); extern double lwpoint_get_m(const LWPOINT *point); /** * Return SRID number */ extern int32_t lwgeom_get_srid(const LWGEOM *geom); /** * Return #LW_TRUE if geometry has Z ordinates */ extern int lwgeom_has_z(const LWGEOM *geom); /** * Return #LW_TRUE if geometry has M ordinates. */ extern int lwgeom_has_m(const LWGEOM *geom); /** * Return #LW_TRUE if geometry has SOLID flag. */ extern int lwgeom_is_solid(const LWGEOM *geom); /** * Return the number of dimensions (2, 3, 4) in a geometry */ extern int lwgeom_ndims(const LWGEOM *geom); /* * Given a point, returns the location of closest point on pointarray * as a fraction of total length (0: first point -- 1: last point). * * If not-null, the third argument will be set to the actual distance * of the point from the pointarray. */ extern double ptarray_locate_point(const POINTARRAY *pa, const POINT4D *pt, double *dist, POINT4D *p_located); /** * Add a measure dimension to a line, interpolating linearly from the start * to the end value. */ extern LWLINE *lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end); extern LWMLINE* lwmline_measured_from_lwmline(const LWMLINE *lwmline, double m_start, double m_end); /** * Determine the location(s) along a measured line where m occurs and * return as a multipoint. Offset to left (positive) or right (negative). */ extern LWGEOM* lwgeom_locate_along(const LWGEOM *lwin, double m, double offset); /** * Determine the segments along a measured line that fall within the m-range * given. Return as a multiline or geometrycollection. * Offset to left (positive) or right (negative). */ extern LWCOLLECTION* lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset); /** * Find the measure value at the location on the line closest to the point. */ extern double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt); /** * Find the time of closest point of approach * * @param mindist if not null will be set to the minimum distance between * the trajectories at the closest point of approach. * * @return the time value in which the minimum distance was reached, -1 * if inputs are invalid (lwerror is called in that case), * -2 if the trajectories do not share any point in time. */ extern double lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist); /** * Is the closest point of approach within a distance ? * * @return LW_TRUE or LW_FALSE */ extern int lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist); /** * Return LW_TRUE or LW_FALSE depending on whether or not a geometry is * a linestring with measure value growing from start to end vertex */ extern int lwgeom_is_trajectory(const LWGEOM *geom); extern int lwline_is_trajectory(const LWLINE *geom); /* * Ensure every segment is at most 'dist' long. * Returned LWGEOM might is unchanged if a POINT. */ extern LWGEOM *lwgeom_segmentize2d(const LWGEOM *line, double dist); extern POINTARRAY *ptarray_segmentize2d(const POINTARRAY *ipa, double dist); extern LWLINE *lwline_segmentize2d(const LWLINE *line, double dist); extern LWPOLY *lwpoly_segmentize2d(const LWPOLY *line, double dist); extern LWCOLLECTION *lwcollection_segmentize2d(const LWCOLLECTION *coll, double dist); /* * Point density functions */ extern LWMPOINT *lwpoly_to_points(const LWPOLY *poly, uint32_t npoints, int32_t seed); extern LWMPOINT *lwmpoly_to_points(const LWMPOLY *mpoly, uint32_t npoints, int32_t seed); extern LWMPOINT *lwgeom_to_points(const LWGEOM *lwgeom, uint32_t npoints, int32_t seed); /* * Geometric median */ extern LWPOINT* lwgeom_median(const LWGEOM *g, double tol, uint32_t maxiter, char fail_if_not_converged); extern LWPOINT* lwmpoint_median(const LWMPOINT *g, double tol, uint32_t maxiter, char fail_if_not_converged); /** * Calculate the GeoHash (http://geohash.org) string for a geometry. Caller must free. */ char *lwgeom_geohash(const LWGEOM *lwgeom, int precision); unsigned int geohash_point_as_int(POINT2D *pt); /** * The return values of lwline_crossing_direction() */ enum CG_LINE_CROSS_TYPE { LINE_NO_CROSS = 0, LINE_CROSS_LEFT = -1, LINE_CROSS_RIGHT = 1, LINE_MULTICROSS_END_LEFT = -2, LINE_MULTICROSS_END_RIGHT = 2, LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3, LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 }; /** * Given two lines, characterize how (and if) they cross each other */ int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2); /** * Given a geometry clip based on the from/to range of one of its ordinates (x, y, z, m). Use for m- and z- clipping. */ LWCOLLECTION* lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset); /** * Macros for specifying GML options. * @{ */ /** For GML3 only, include srsDimension attribute in output */ #define LW_GML_IS_DIMS (1<<0) /** For GML3 only, declare that datas are lat/lon. Swaps axis order */ #define LW_GML_IS_DEGREE (1<<1) /** For GML3, use rather than for lines */ #define LW_GML_SHORTLINE (1<<2) /** For GML2 and GML3, output only extent of geometry */ #define LW_GML_EXTENT (1<<4) #define IS_DIMS(x) ((x) & LW_GML_IS_DIMS) #define IS_DEGREE(x) ((x) & LW_GML_IS_DEGREE) /** @} */ /** * Macros for specifying X3D options. * @{ */ /** For flip X/Y coordinates to Y/X */ #define LW_X3D_FLIP_XY (1<<0) #define LW_X3D_USE_GEOCOORDS (1<<1) #define X3D_USE_GEOCOORDS(x) ((x) & LW_X3D_USE_GEOCOORDS) extern char* lwgeom_to_gml2(const LWGEOM *geom, const char *srs, int precision, const char *prefix); extern char* lwgeom_extent_to_gml2(const LWGEOM *geom, const char *srs, int precision, const char *prefix); /** * @param opts output options bitfield, see LW_GML macros for meaning */ extern char* lwgeom_extent_to_gml3(const LWGEOM *geom, const char *srs, int precision, int opts, const char *prefix); extern char* lwgeom_to_gml3(const LWGEOM *geom, const char *srs, int precision, int opts, const char *prefix, const char *id); extern char* lwgeom_to_kml2(const LWGEOM *geom, int precision, const char *prefix); extern char* lwgeom_to_geojson(const LWGEOM *geo, char *srs, int precision, int has_bbox); extern char* lwgeom_to_svg(const LWGEOM *geom, int precision, int relative); extern char* lwgeom_to_x3d3(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid); extern char* lwgeom_to_encoded_polyline(const LWGEOM *geom, int precision); /** * Create an LWGEOM object from a GeoJSON representation * * @param geojson the GeoJSON input * @param srs output parameter. Will be set to a newly allocated * string holding the spatial reference string, or NULL * if no such parameter is found in input. * If not null, the pointer must be freed with lwfree. */ extern LWGEOM* lwgeom_from_geojson(const char *geojson, char **srs); /** * Create an LWGEOM object from an Encoded Polyline representation * * @param encodedpolyline the Encoded Polyline input */ extern LWGEOM* lwgeom_from_encoded_polyline(const char *encodedpolyline, int precision); /** * Initialize a spheroid object for use in geodetic functions. */ extern void spheroid_init(SPHEROID *s, double a, double b); /** * Calculate the geodetic distance from lwgeom1 to lwgeom2 on the spheroid. * A spheroid with major axis == minor axis will be treated as a sphere. * Pass in a tolerance in spheroid units. */ extern double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance); /** * Calculate the location of a point on a spheroid, give a start point, bearing and distance. */ extern LWPOINT* lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth); /** * Derive a new geometry with vertices added to ensure no vertex is more * than max_seg_length (in radians) from any other vertex. */ extern LWGEOM* lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length); /** * Calculate the bearing between two points on a spheroid. */ extern double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid); /** * Calculate the geodetic area of a lwgeom on the sphere. The result * will be multiplied by the average radius of the supplied spheroid. */ extern double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid); /** * Calculate the geodetic area of a lwgeom on the spheroid. The result * will have the squared units of the spheroid axes. */ extern double lwgeom_area_spheroid(const LWGEOM *lwgeom, const SPHEROID *spheroid); /** * Calculate the geodetic length of a lwgeom on the unit sphere. The result * will have to by multiplied by the real radius to get the real length. */ extern double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s); /** * Calculate covers predicate for two lwgeoms on the sphere. Currently * only handles point-in-polygon. */ extern int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2); typedef struct { POINT2D* center; double radius; } LWBOUNDINGCIRCLE; extern void lwboundingcircle_destroy(LWBOUNDINGCIRCLE* c); /* Calculates the minimum circle that encloses all of the points in g, using a * two-dimensional implementation of the algorithm proposed in: * * Welzl, Emo (1991), "Smallest enclosing disks (balls and elipsoids)." * New Results and Trends in Computer Science (H. Maurer, Ed.), Lecture Notes * in Computer Science, 555 (1991) 359-370. * * Available online at the time of this writing at * https://www.inf.ethz.ch/personal/emo/PublFiles/SmallEnclDisk_LNCS555_91.pdf * * Returns NULL if the circle could not be calculated. */ extern LWBOUNDINGCIRCLE* lwgeom_calculate_mbc(const LWGEOM* g); /** * Swap ordinate values in every vertex of the geometry. * * Ordinates to swap are specified using an index with meaning: * 0=x, 1=y, 2=z, 3=m * * Swapping an existing ordinate with an unexisting one results * in undefined value being written in the existing ordinate. * Caller should verify and prevent such calls. * * Availability: 2.2.0 */ extern void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2); struct LWPOINTITERATOR; typedef struct LWPOINTITERATOR LWPOINTITERATOR; /** * Create a new LWPOINTITERATOR over supplied LWGEOM* */ extern LWPOINTITERATOR* lwpointiterator_create(const LWGEOM* g); /** * Create a new LWPOINTITERATOR over supplied LWGEOM* * Supports modification of coordinates during iteration. */ extern LWPOINTITERATOR* lwpointiterator_create_rw(LWGEOM* g); /** * Free all memory associated with the iterator */ extern void lwpointiterator_destroy(LWPOINTITERATOR* s); /** * Returns LW_TRUE if there is another point available in the iterator. */ extern int lwpointiterator_has_next(LWPOINTITERATOR* s); /** * Attempts to replace the next point int the iterator with p, and advances * the iterator to the next point. * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise. * */ extern int lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p); /** * Attempts to assign the next point in the iterator to p, and advances * the iterator to the next point. If p is NULL, the iterator will be * advanced without reading a point. * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise. * */ extern int lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p); /** * Attempts to assigns the next point in the iterator to p. Does not advance. * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise. */ extern int lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p); /** * Convert a single hex digit into the corresponding char */ extern uint8_t parse_hex(char *str); /** * Convert a char into a human readable hex digit */ extern void deparse_hex(uint8_t str, char *result); /*********************************************************************** ** Functions for managing serialized forms and bounding boxes. */ /** * Check that coordinates of LWGEOM are all within the geodetic range (-180, -90, 180, 90) */ extern int lwgeom_check_geodetic(const LWGEOM *geom); /** * Gently move coordinates of LWGEOM if they are close enough into geodetic range. */ extern int lwgeom_nudge_geodetic(LWGEOM *geom); /** * Force coordinates of LWGEOM into geodetic range (-180, -90, 180, 90) */ extern int lwgeom_force_geodetic(LWGEOM *geom); /** * Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists */ extern void lwgeom_set_geodetic(LWGEOM *geom, int value); /** * Calculate the geodetic bounding box for an LWGEOM. Z/M coordinates are * ignored for this calculation. Pass in non-null, geodetic bounding box for function * to fill out. LWGEOM must have been built from a GSERIALIZED to provide * double aligned point arrays. */ extern int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox); /** * Calculate the 2-4D bounding box of a geometry. Z/M coordinates are honored * for this calculation, though for curves they are not included in calculations * of curvature. */ extern int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox); /** * Calculate bounding box of a geometry, automatically taking into account * whether it is cartesian or geodetic. */ extern int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox); /** * New function to read doubles directly from the double* coordinate array * of an aligned lwgeom #POINTARRAY (built by de-serializing a #GSERIALIZED). */ extern int getPoint2d_p_ro(const POINTARRAY *pa, uint32_t n, POINT2D **point); /** * Calculate geodetic (x/y/z) box and add values to gbox. Return #LW_SUCCESS on success. */ extern int ptarray_calculate_gbox_geodetic(const POINTARRAY *pa, GBOX *gbox); /** * Calculate box (x/y) and add values to gbox. Return #LW_SUCCESS on success. */ extern int ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox ); /** * Calculate a spherical point that falls outside the geocentric gbox */ int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside); /** * Create a new gbox with the dimensionality indicated by the flags. Caller * is responsible for freeing. */ extern GBOX* gbox_new(lwflags_t flags); /** * Zero out all the entries in the #GBOX. Useful for cleaning * statically allocated gboxes. */ extern void gbox_init(GBOX *gbox); /** * Update the merged #GBOX to be large enough to include itself and the new box. */ extern int gbox_merge(const GBOX *new_box, GBOX *merged_box); /** * Update the output #GBOX to be large enough to include both inputs. */ extern int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout); /** * Move the box minimums down and the maximums up by the distance provided. */ extern void gbox_expand(GBOX *g, double d); /** * Move the box minimums down and the maximums up by the distances provided. */ extern void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm); /** * Initialize a #GBOX using the values of the point. */ extern int gbox_init_point3d(const POINT3D *p, GBOX *gbox); /** * Update the #GBOX to be large enough to include itself and the new point. */ extern int gbox_merge_point3d(const POINT3D *p, GBOX *gbox); /** * Return true if the point is inside the gbox */ extern int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt); /** * Allocate a string representation of the #GBOX, based on dimensionality of flags. */ extern char* gbox_to_string(const GBOX *gbox); /** * Return a copy of the #GBOX, based on dimensionality of flags. */ extern GBOX* gbox_copy(const GBOX *gbox); /** * Warning, do not use this function, it is very particular about inputs. */ extern GBOX* gbox_from_string(const char *str); /** * Return #LW_TRUE if the #GBOX overlaps, #LW_FALSE otherwise. */ extern int gbox_overlaps(const GBOX *g1, const GBOX *g2); /** * Return #LW_TRUE if the #GBOX overlaps on the 2d plane, #LW_FALSE otherwise. */ extern int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2); /** * Return #LW_TRUE if the first #GBOX contains the second on the 2d plane, #LW_FALSE otherwise. */ extern int gbox_contains_2d(const GBOX *g1, const GBOX *g2); /** * Copy the values of original #GBOX into duplicate. */ extern void gbox_duplicate(const GBOX *original, GBOX *duplicate); /** * Return the number of bytes necessary to hold a #GBOX of this dimension in * serialized form. */ extern size_t gbox_serialized_size(lwflags_t flags); /** * Check if 2 given Gbox are the same */ extern int gbox_same(const GBOX *g1, const GBOX *g2); /** * Check if 2 given GBOX are the same in x and y */ extern int gbox_same_2d(const GBOX *g1, const GBOX *g2); /** * Check if two given GBOX are the same in x and y, or would round to the same * GBOX in x and if serialized in GSERIALIZED */ extern int gbox_same_2d_float(const GBOX *g1, const GBOX *g2); /** * Round given GBOX to float boundaries * * This turns a GBOX into the version it would become * after a serialize/deserialize round trip. */ extern void gbox_float_round(GBOX *gbox); /** * Return false if any of the dimensions is NaN or infinite */ extern int gbox_is_valid(const GBOX *gbox); /** * Return a sortable key based on the center point of the GBOX. */ extern uint64_t gbox_get_sortable_hash(const GBOX *g, const int32_t srid); /** * Return a sortable key based on gserialized. */ extern uint64_t gserialized_get_sortable_hash(const GSERIALIZED *g); /** * Utility function to get type number from string. For example, a string 'POINTZ' * would return type of 1 and z of 1 and m of 0. Valid */ extern int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m); /** * Parser check flags * * @see lwgeom_from_wkb * @see lwgeom_from_hexwkb * @see lwgeom_parse_wkt */ #define LW_PARSER_CHECK_MINPOINTS 1 #define LW_PARSER_CHECK_ODD 2 #define LW_PARSER_CHECK_CLOSURE 4 #define LW_PARSER_CHECK_ZCLOSURE 8 #define LW_PARSER_CHECK_NONE 0 #define LW_PARSER_CHECK_ALL (LW_PARSER_CHECK_MINPOINTS | LW_PARSER_CHECK_ODD | LW_PARSER_CHECK_CLOSURE) /** * Parser result structure: returns the result of attempting to convert * (E)WKT/(E)WKB to LWGEOM */ typedef struct struct_lwgeom_parser_result { const char *wkinput; /* Copy of pointer to input WKT/WKB */ uint8_t *serialized_lwgeom; /* Pointer to serialized LWGEOM */ size_t size; /* Size of serialized LWGEOM in bytes */ LWGEOM *geom; /* Pointer to LWGEOM struct */ const char *message; /* Error/warning message */ int errcode; /* Error/warning number */ int errlocation; /* Location of error */ int parser_check_flags; /* Bitmask of validity checks run during this parse */ } LWGEOM_PARSER_RESULT; /* * Parser error messages (these must match the message array in lwgparse.c) */ #define PARSER_ERROR_MOREPOINTS 1 #define PARSER_ERROR_ODDPOINTS 2 #define PARSER_ERROR_UNCLOSED 3 #define PARSER_ERROR_MIXDIMS 4 #define PARSER_ERROR_INVALIDGEOM 5 #define PARSER_ERROR_INVALIDWKBTYPE 6 #define PARSER_ERROR_INCONTINUOUS 7 #define PARSER_ERROR_TRIANGLEPOINTS 8 #define PARSER_ERROR_LESSPOINTS 9 #define PARSER_ERROR_OTHER 10 /* * Unparser result structure: returns the result of attempting to convert LWGEOM to (E)WKT/(E)WKB */ typedef struct struct_lwgeom_unparser_result { uint8_t *serialized_lwgeom; /* Copy of pointer to input serialized LWGEOM */ char *wkoutput; /* Pointer to WKT or WKB output */ size_t size; /* Size of serialized LWGEOM in bytes */ const char *message; /* Error/warning message */ int errlocation; /* Location of error */ } LWGEOM_UNPARSER_RESULT; /* * Unparser error messages (these must match the message array in lwgunparse.c) */ #define UNPARSER_ERROR_MOREPOINTS 1 #define UNPARSER_ERROR_ODDPOINTS 2 #define UNPARSER_ERROR_UNCLOSED 3 /* ** Variants available for WKB and WKT output types */ #define WKB_ISO 0x01 #define WKB_SFSQL 0x02 #define WKB_EXTENDED 0x04 #define WKB_NDR 0x08 #define WKB_XDR 0x10 #define WKB_HEX 0x20 #define WKB_NO_NPOINTS 0x40 /* Internal use only */ #define WKB_NO_SRID 0x80 /* Internal use only */ #define WKT_ISO 0x01 #define WKT_SFSQL 0x02 #define WKT_EXTENDED 0x04 /* ** Variants available for TWKB */ #define TWKB_BBOX 0x01 /* User wants bboxes */ #define TWKB_SIZE 0x02 /* User wants sizes */ #define TWKB_ID 0x04 /* User wants id */ #define TWKB_NO_TYPE 0x10 /* No type because it is a sub geometry */ #define TWKB_NO_ID 0x20 /* No ID because it is a subgeometry */ #define TWKB_DEFAULT_PRECISION 0 /* Aim for 1m (or ft) rounding by default */ /* ** New parsing and unparsing functions. */ /** * @param lwgeom geometry to convert to WKT * @param variant output format to use (WKT_ISO, WKT_SFSQL, WKT_EXTENDED) */ extern char* lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out); /** * @param lwgeom geometry to convert to WKT * @param variant output format to use * (WKB_ISO, WKB_SFSQL, WKB_EXTENDED, WKB_NDR, WKB_XDR) */ extern uint8_t* lwgeom_to_wkb(const LWGEOM *geom, uint8_t variant, size_t *size_out); /** * @param lwgeom geometry to convert to HEXWKB * @param variant output format to use * (WKB_ISO, WKB_SFSQL, WKB_EXTENDED, WKB_NDR, WKB_XDR) */ extern char* lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out); /** * @param lwgeom geometry to convert to EWKT */ extern char *lwgeom_to_ewkt(const LWGEOM *lwgeom); /** * @param check parser check flags, see LW_PARSER_CHECK_* macros * @param size length of WKB byte buffer * @param wkb WKB byte buffer */ extern LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check); /** * @param wkt WKT string * @param check parser check flags, see LW_PARSER_CHECK_* macros */ extern LWGEOM* lwgeom_from_wkt(const char *wkt, const char check); /** * @param check parser check flags, see LW_PARSER_CHECK_* macros */ extern LWGEOM* lwgeom_from_hexwkb(const char *hexwkb, const char check); extern uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize); extern char* hexbytes_from_bytes(const uint8_t *bytes, size_t size); /* * WKT detailed parsing support */ extern int lwgeom_parse_wkt(LWGEOM_PARSER_RESULT *parser_result, char *wktstr, int parse_flags); void lwgeom_parser_result_init(LWGEOM_PARSER_RESULT *parser_result); void lwgeom_parser_result_free(LWGEOM_PARSER_RESULT *parser_result); /* Memory management */ extern void *lwalloc(size_t size); extern void *lwrealloc(void *mem, size_t size); extern void lwfree(void *mem); /* Utilities */ extern char *lwmessage_truncate(char *str, int startpos, int endpos, int maxlength, int truncdirection); /* * TWKB functions */ /** * @param check parser check flags, see LW_PARSER_CHECK_* macros * @param size parser check flags, see LW_PARSER_CHECK_* macros */ extern LWGEOM* lwgeom_from_twkb(const uint8_t *twkb, size_t twkb_size, char check); /** * @param geom input geometry * @param variant what variations on TWKB are requested? * @param twkb_size returns the length of the output TWKB in bytes if set */ extern uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size); extern uint8_t* lwgeom_to_twkb_with_idlist(const LWGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size); /** * Trim the bits of an LWGEOM in place, to optimize it for compression. * Sets all bits to zero that are not required to maintain a specified * number of digits after the decimal point. Negative precision values * indicate digits before the decimal point do not need to be preserved. * * @param geom input geometry * @param prec_x precision (digits after decimal point) in x dimension * @param prec_y precision (digits after decimal point) in y dimension * @param prec_z precision (digits after decimal point) in z dimension * @param prec_m precision (digits after decimal point) in m dimension */ extern void lwgeom_trim_bits_in_place(LWGEOM *geom, int32_t prec_x, int32_t prec_y, int32_t prec_z, int32_t prec_m); /******************************************************************************* * SQLMM internal functions ******************************************************************************/ int lwgeom_has_arc(const LWGEOM *geom); LWGEOM *lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad); LWGEOM *lwgeom_unstroke(const LWGEOM *geom); /** * Semantic of the `tolerance` argument passed to * lwcurve_linearize */ typedef enum { /** * Tolerance expresses the number of segments to use * for each quarter of circle (quadrant). Must be * an integer. */ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD = 0, /** * Tolerance expresses the maximum distance between * an arbitrary point on the curve and the closest * point to it on the resulting approximation, in * cartesian units. */ LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION = 1, /** * Tolerance expresses the maximum angle between * the radii generating approximation line vertices, * given in radiuses. A value of 1 would result * in an approximation of a semicircle composed by * 180 segments */ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE = 2 } LW_LINEARIZE_TOLERANCE_TYPE; typedef enum { /** * Symmetric linearization means that the output * vertices would be the same no matter the order * of the points defining the input curve. */ LW_LINEARIZE_FLAG_SYMMETRIC = 1 << 0, /** * Retain angle instructs the engine to try its best * to retain the requested angle between generating * radii (where angle can be given explicitly with * LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE or implicitly * with LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD or * LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION). * * It only makes sense with LW_LINEARIZE_FLAG_SYMMETRIC * which would otherwise reduce the angle as needed to * keep it constant among all radiis so that all * segments are of the same length. * * When this flag is set, the first and last generating * angles (and thus the first and last segments) may * instead be smaller (shorter) than the others. * */ LW_LINEARIZE_FLAG_RETAIN_ANGLE = 1 << 1 } LW_LINEARIZE_FLAGS; /** * @param geom input geometry * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags bitwise OR of operational flags, see LW_LINEARIZE_FLAGS * * @return a newly allocated LWGEOM */ extern LWGEOM* lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags); /******************************************************************************* * GEOS proxy functions on LWGEOM ******************************************************************************/ /** Return GEOS version string (not to be freed) */ const char* lwgeom_geos_version(void); /** Convert an LWGEOM to a GEOS Geometry and convert back -- for debug only */ LWGEOM* lwgeom_geos_noop(const LWGEOM *geom) ; LWGEOM *lwgeom_normalize(const LWGEOM *geom); LWGEOM *lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2); LWGEOM *lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2); LWGEOM *lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2); LWGEOM *lwgeom_pointonsurface(const LWGEOM* geom); LWGEOM *lwgeom_centroid(const LWGEOM* geom); LWGEOM *lwgeom_union(const LWGEOM *geom1, const LWGEOM *geom2); LWGEOM *lwgeom_linemerge(const LWGEOM *geom1); LWGEOM *lwgeom_unaryunion(const LWGEOM *geom1); LWGEOM *lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1); LWCOLLECTION *lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices); /** * Snap vertices and segments of a geometry to another using a given tolerance. * * @param geom1 the geometry to snap * @param geom2 the geometry to snap to * @param tolerance the distance under which vertices and segments are snapped */ LWGEOM* lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance); /* * Return the set of paths shared between two linear geometries, * and their direction (same or opposite). * * @param geom1 a lineal geometry * @param geom2 another lineal geometry */ LWGEOM* lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2); /* * An offset curve against the input line. * * @param geom a lineal geometry or collection of them * @param size offset distance. Offset left if negative and right if positive * @param quadsegs number of quadrature segments in curves (try 8) * @param joinStyle (1 = round, 2 = mitre, 3 = bevel) * @param mitreLimit (try 5.0) * @return derived geometry (linestring or multilinestring) * */ LWGEOM* lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit); /* * Return true if the input geometry is "simple" as per OGC defn. * * @return 1 if simple, 0 if non-simple, -1 on exception (lwerror is called * in that case) */ int lwgeom_is_simple(const LWGEOM *lwgeom); /******************************************************************************* * PROJ4-dependent extra functions on LWGEOM ******************************************************************************/ int lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr); #if POSTGIS_PROJ_VERSION < 60 /** * Get a projection from a string representation * * Eg: "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" */ projPJ projpj_from_string(const char* txt); #endif /** * Transform (reproject) a geometry in-place. * @param geom the geometry to transform * @param PJ the input and output */ int lwgeom_transform(LWGEOM *geom, LWPROJ* pj); int ptarray_transform(POINTARRAY *pa, LWPROJ* pj); #if POSTGIS_PROJ_VERSION >= 60 /** * Allocate a new LWPROJ containing the reference to the PROJ's PJ * If extra_geography_data is true, it will generate the following values for * the source srs: is_latlong (geometric or not) and spheroid values */ LWPROJ *lwproj_from_PJ(PJ *pj, int8_t extra_geography_data); #endif /******************************************************************************* * GEOS-dependent extra functions on LWGEOM ******************************************************************************/ /** * Take a geometry and return an areal geometry * (Polygon or MultiPolygon). * Actually a wrapper around GEOSpolygonize, * transforming the resulting collection into * a valid polygon Geometry. */ LWGEOM* lwgeom_buildarea(const LWGEOM *geom) ; /** * Attempts to make an invalid geometries valid w/out losing points. */ LWGEOM* lwgeom_make_valid(LWGEOM* geom); /* * Split (multi)polygon by line; (multi)line by (multi)line, * (multi)point or (multi)polygon boundary. * * Collections are accepted as first argument. * Returns all obtained pieces as a collection. */ LWGEOM* lwgeom_split(const LWGEOM* lwgeom_in, const LWGEOM* blade_in); /* * Fully node a set of linestrings, using the least nodes preserving * all the input ones. */ LWGEOM* lwgeom_node(const LWGEOM* lwgeom_in); /** * Take vertices of a geometry and build a delaunay * triangulation on them. * * @param geom the input geometry * @param tolerance an optional snapping tolerance for improved robustness * @param edgeOnly if non-zero the result will be a MULTILINESTRING, * otherwise it'll be a COLLECTION of polygons. */ LWGEOM* lwgeom_delaunay_triangulation(const LWGEOM *geom, double tolerance, int32_t edgeOnly); /** * Take vertices of a geometry and build the Voronoi diagram * * @param g the input geometry * @param env an optional envelope for clipping the results * @param tolerance an optional snapping tolerance for improved robustness * @param output_edges if non-zero the result will be a MULTILINESTRING, * otherwise it'll be a COLLECTION of polygons. */ LWGEOM* lwgeom_voronoi_diagram(const LWGEOM* g, const GBOX* env, double tolerance, int output_edges); /** * Take a list of LWGEOMs and a number of clusters and return an integer * array indicating which cluster each geometry is in. * * @param geoms the input array of LWGEOM pointers * @param ngeoms the number of elements in the array * @param k the number of clusters to calculate */ int * lwgeom_cluster_2d_kmeans(const LWGEOM **geoms, uint32_t ngeoms, uint32_t k); #include "lwinline.h" #endif /* !defined _LIBLWGEOM_H */ lwgeom/src/liblwgeom/lwtin.c0000644000176200001440000001014013773172540015622 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" LWTIN* lwtin_add_lwtriangle(LWTIN *mobj, const LWTRIANGLE *obj) { return (LWTIN*)lwcollection_add_lwgeom((LWCOLLECTION*)mobj, (LWGEOM*)obj); } void lwtin_free(LWTIN *tin) { uint32_t i; if ( ! tin ) return; if ( tin->bbox ) lwfree(tin->bbox); for ( i = 0; i < tin->ngeoms; i++ ) if ( tin->geoms && tin->geoms[i] ) lwtriangle_free(tin->geoms[i]); if ( tin->geoms ) lwfree(tin->geoms); lwfree(tin); } void printLWTIN(LWTIN *tin) { uint32_t i; LWTRIANGLE *triangle; if (tin->type != TINTYPE) lwerror("printLWTIN called with something else than a TIN"); lwnotice("LWTIN {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(tin->flags)); lwnotice(" SRID = %i", (int)tin->srid); lwnotice(" ngeoms = %i", (int)tin->ngeoms); for (i=0; ingeoms; i++) { triangle = (LWTRIANGLE *) tin->geoms[i]; printPA(triangle->points); } lwnotice("}"); } /* * TODO rewrite all this stuff to be based on a truly topological model */ struct struct_tin_arcs { double ax, ay, az; double bx, by, bz; uint32_t cnt, face; }; typedef struct struct_tin_arcs *tin_arcs; /* We supposed that the geometry is valid we could have wrong result if not */ int lwtin_is_closed(const LWTIN *tin) { uint32_t i, j, k; uint32_t narcs, carc; int found; tin_arcs arcs; POINT4D pa, pb; LWTRIANGLE *patch; /* If surface is not 3D, it's can't be closed */ if (!FLAGS_GET_Z(tin->flags)) return 0; /* Max theoretical arcs number if no one is shared ... */ narcs = 3 * tin->ngeoms; arcs = lwalloc(sizeof(struct struct_tin_arcs) * narcs); for (i=0, carc=0; i < tin->ngeoms ; i++) { patch = (LWTRIANGLE *) tin->geoms[i]; for (j=0; j < 3 ; j++) { getPoint4d_p(patch->points, j, &pa); getPoint4d_p(patch->points, j+1, &pb); /* Make sure to order the 'lower' point first */ if ( (pa.x > pb.x) || (pa.x == pb.x && pa.y > pb.y) || (pa.x == pb.x && pa.y == pb.y && pa.z > pb.z) ) { pa = pb; getPoint4d_p(patch->points, j, &pb); } for (found=0, k=0; k < carc ; k++) { if ( ( arcs[k].ax == pa.x && arcs[k].ay == pa.y && arcs[k].az == pa.z && arcs[k].bx == pb.x && arcs[k].by == pb.y && arcs[k].bz == pb.z && arcs[k].face != i) ) { arcs[k].cnt++; found = 1; /* Look like an invalid TIN anyway not a closed one */ if (arcs[k].cnt > 2) { lwfree(arcs); return 0; } } } if (!found) { arcs[carc].cnt=1; arcs[carc].face=i; arcs[carc].ax = pa.x; arcs[carc].ay = pa.y; arcs[carc].az = pa.z; arcs[carc].bx = pb.x; arcs[carc].by = pb.y; arcs[carc].bz = pb.z; carc++; /* Look like an invalid TIN anyway not a closed one */ if (carc > narcs) { lwfree(arcs); return 0; } } } } /* A TIN is closed if each edge is shared by exactly 2 faces */ for (k=0; k < carc ; k++) { if (arcs[k].cnt != 2) { lwfree(arcs); return 0; } } lwfree(arcs); /* Invalid TIN case */ if (carc < tin->ngeoms) return 0; return 1; } lwgeom/src/liblwgeom/lwutil.c0000644000176200001440000003233114127263115016004 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2004-2015 Sandro Santilli * Copyright (C) 2006 Mark Leslie * Copyright (C) 2008-2009 Mark Cave-Ayland * Copyright (C) 2009-2015 Paul Ramsey * Copyright (C) 2010 Olivier Courtin * **********************************************************************/ #include #include #include #include #include /* for tolower */ /* Global variables */ #include "../postgis_config.h" #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "R.h" /* Default allocators */ static void * default_allocator(size_t size); static void default_freeor(void *mem); static void * default_reallocator(void *mem, size_t size); lwallocator lwalloc_var = default_allocator; lwreallocator lwrealloc_var = default_reallocator; lwfreeor lwfree_var = default_freeor; /* Default reporters */ static void default_noticereporter(const char *fmt, va_list ap); static void default_errorreporter(const char *fmt, va_list ap); lwreporter lwnotice_var = default_noticereporter; lwreporter lwerror_var = default_errorreporter; /* Default logger */ static void default_debuglogger(int level, const char *fmt, va_list ap); lwdebuglogger lwdebug_var = default_debuglogger; #define LW_MSG_MAXLEN 256 static char *lwgeomTypeName[] = { "Unknown", "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon", "GeometryCollection", "CircularString", "CompoundCurve", "CurvePolygon", "MultiCurve", "MultiSurface", "PolyhedralSurface", "Triangle", "Tin" }; /* * Default allocators * * We include some default allocators that use malloc/free/realloc * along with stdout/stderr since this is the most common use case * */ static void * default_allocator(size_t size) { void *mem = malloc(size); return mem; } static void default_freeor(void *mem) { free(mem); } static void * default_reallocator(void *mem, size_t size) { void *ret = realloc(mem, size); return ret; } /* * Default lwnotice/lwerror handlers * * Since variadic functions cannot pass their parameters directly, we need * wrappers for these functions to convert the arguments into a va_list * structure. */ static void default_noticereporter(const char *fmt, va_list ap) { char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; // fprintf(stderr, "%s\n", msg); Rprintf("%s\n", msg); } static void default_debuglogger(int level, const char *fmt, va_list ap) { char msg[LW_MSG_MAXLEN+1]; if ( POSTGIS_DEBUG_LEVEL >= level ) { /* Space pad the debug output */ int i; for ( i = 0; i < level; i++ ) msg[i] = ' '; vsnprintf(msg+i, LW_MSG_MAXLEN-i, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; // fprintf(stderr, "%s\n", msg); Rprintf("%s\n", msg); } } static void default_errorreporter(const char *fmt, va_list ap) { char msg[LW_MSG_MAXLEN+1]; vsnprintf (msg, LW_MSG_MAXLEN, fmt, ap); msg[LW_MSG_MAXLEN]='\0'; // fprintf(stderr, "%s\n", msg); // exit(1); error("%s\n", msg); } /** * This function is called by programs which want to set up custom handling * for memory management and error reporting * * Only non-NULL values change their respective handler */ void lwgeom_set_handlers(lwallocator allocator, lwreallocator reallocator, lwfreeor freeor, lwreporter errorreporter, lwreporter noticereporter) { if ( allocator ) lwalloc_var = allocator; if ( reallocator ) lwrealloc_var = reallocator; if ( freeor ) lwfree_var = freeor; if ( errorreporter ) lwerror_var = errorreporter; if ( noticereporter ) lwnotice_var = noticereporter; } void lwgeom_set_debuglogger(lwdebuglogger debuglogger) { if ( debuglogger ) lwdebug_var = debuglogger; } void lwnotice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*lwnotice_var)(fmt, ap); va_end(ap); } void lwerror(const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*lwerror_var)(fmt, ap); va_end(ap); } void lwdebug(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*lwdebug_var)(level, fmt, ap); va_end(ap); } const char* lwtype_name(uint8_t type) { if ( type > 15 ) { /* assert(0); */ return "Invalid type"; } return lwgeomTypeName[(int ) type]; } void * lwalloc(size_t size) { void *mem = lwalloc_var(size); LWDEBUGF(5, "lwalloc: %d@%p", size, mem); return mem; } void * lwrealloc(void *mem, size_t size) { LWDEBUGF(5, "lwrealloc: %d@%p", size, mem); return lwrealloc_var(mem, size); } void lwfree(void *mem) { lwfree_var(mem); } char * lwstrdup(const char* a) { size_t l = strlen(a)+1; char *b = lwalloc(l); strncpy(b, a, l); return b; } /* * Returns a new string which contains a maximum of maxlength characters starting * from startpos and finishing at endpos (0-based indexing). If the string is * truncated then the first or last characters are replaced by "..." as * appropriate. * * The caller should specify start or end truncation by setting the truncdirection * parameter as follows: * 0 - start truncation (i.e. characters are removed from the beginning) * 1 - end truncation (i.e. characters are removed from the end) */ char *lwmessage_truncate(char *str, int startpos, int endpos, int maxlength, int truncdirection) { char *output; char *outstart; /* Allocate space for new string */ output = lwalloc(maxlength + 4); output[0] = '\0'; /* Start truncation */ if (truncdirection == 0) { /* Calculate the start position */ if (endpos - startpos < maxlength) { outstart = str + startpos; strncat(output, outstart, endpos - startpos + 1); } else { if (maxlength >= 3) { /* Add "..." prefix */ outstart = str + endpos + 1 - maxlength + 3; strncat(output, "...", 4); strncat(output, outstart, maxlength - 3); } else { /* maxlength is too small; just output "..." */ strncat(output, "...", 4); } } } /* End truncation */ if (truncdirection == 1) { /* Calculate the end position */ if (endpos - startpos < maxlength) { outstart = str + startpos; strncat(output, outstart, endpos - startpos + 1); } else { if (maxlength >= 3) { /* Add "..." suffix */ outstart = str + startpos; strncat(output, outstart, maxlength - 3); strncat(output, "...", 4); } else { /* maxlength is too small; just output "..." */ strncat(output, "...", 4); } } } return output; } int32_t clamp_srid(int32_t srid) { int newsrid = srid; if ( newsrid <= 0 ) { if ( newsrid != SRID_UNKNOWN ) { newsrid = SRID_UNKNOWN; lwnotice("SRID value %d converted to the officially unknown SRID value %d", srid, newsrid); } } else if ( srid > SRID_MAXIMUM ) { newsrid = SRID_USER_MAXIMUM + 1 + /* -1 is to reduce likelyhood of clashes */ /* NOTE: must match implementation in postgis_restore.pl */ ( srid % ( SRID_MAXIMUM - SRID_USER_MAXIMUM - 1 ) ); lwnotice("SRID value %d > SRID_MAXIMUM converted to %d", srid, newsrid); } return newsrid; } /* Structure for the type array */ struct geomtype_struct { char *typename; int type; int z; int m; }; /* Type array. Note that the order of this array is important in that any typename in the list must *NOT* occur within an entry before it. Otherwise if we search for "POINT" at the top of the list we would also match MULTIPOINT, for example. */ struct geomtype_struct geomtype_struct_array[] = { { "GEOMETRYCOLLECTIONZM", COLLECTIONTYPE, 1, 1 }, { "GEOMETRYCOLLECTIONZ", COLLECTIONTYPE, 1, 0 }, { "GEOMETRYCOLLECTIONM", COLLECTIONTYPE, 0, 1 }, { "GEOMETRYCOLLECTION", COLLECTIONTYPE, 0, 0 }, { "GEOMETRYZM", 0, 1, 1 }, { "GEOMETRYZ", 0, 1, 0 }, { "GEOMETRYM", 0, 0, 1 }, { "GEOMETRY", 0, 0, 0 }, { "POLYHEDRALSURFACEZM", POLYHEDRALSURFACETYPE, 1, 1 }, { "POLYHEDRALSURFACEZ", POLYHEDRALSURFACETYPE, 1, 0 }, { "POLYHEDRALSURFACEM", POLYHEDRALSURFACETYPE, 0, 1 }, { "POLYHEDRALSURFACE", POLYHEDRALSURFACETYPE, 0, 0 }, { "TINZM", TINTYPE, 1, 1 }, { "TINZ", TINTYPE, 1, 0 }, { "TINM", TINTYPE, 0, 1 }, { "TIN", TINTYPE, 0, 0 }, { "CIRCULARSTRINGZM", CIRCSTRINGTYPE, 1, 1 }, { "CIRCULARSTRINGZ", CIRCSTRINGTYPE, 1, 0 }, { "CIRCULARSTRINGM", CIRCSTRINGTYPE, 0, 1 }, { "CIRCULARSTRING", CIRCSTRINGTYPE, 0, 0 }, { "COMPOUNDCURVEZM", COMPOUNDTYPE, 1, 1 }, { "COMPOUNDCURVEZ", COMPOUNDTYPE, 1, 0 }, { "COMPOUNDCURVEM", COMPOUNDTYPE, 0, 1 }, { "COMPOUNDCURVE", COMPOUNDTYPE, 0, 0 }, { "CURVEPOLYGONZM", CURVEPOLYTYPE, 1, 1 }, { "CURVEPOLYGONZ", CURVEPOLYTYPE, 1, 0 }, { "CURVEPOLYGONM", CURVEPOLYTYPE, 0, 1 }, { "CURVEPOLYGON", CURVEPOLYTYPE, 0, 0 }, { "MULTICURVEZM", MULTICURVETYPE, 1, 1 }, { "MULTICURVEZ", MULTICURVETYPE, 1, 0 }, { "MULTICURVEM", MULTICURVETYPE, 0, 1 }, { "MULTICURVE", MULTICURVETYPE, 0, 0 }, { "MULTISURFACEZM", MULTISURFACETYPE, 1, 1 }, { "MULTISURFACEZ", MULTISURFACETYPE, 1, 0 }, { "MULTISURFACEM", MULTISURFACETYPE, 0, 1 }, { "MULTISURFACE", MULTISURFACETYPE, 0, 0 }, { "MULTILINESTRINGZM", MULTILINETYPE, 1, 1 }, { "MULTILINESTRINGZ", MULTILINETYPE, 1, 0 }, { "MULTILINESTRINGM", MULTILINETYPE, 0, 1 }, { "MULTILINESTRING", MULTILINETYPE, 0, 0 }, { "MULTIPOLYGONZM", MULTIPOLYGONTYPE, 1, 1 }, { "MULTIPOLYGONZ", MULTIPOLYGONTYPE, 1, 0 }, { "MULTIPOLYGONM", MULTIPOLYGONTYPE, 0, 1 }, { "MULTIPOLYGON", MULTIPOLYGONTYPE, 0, 0 }, { "MULTIPOINTZM", MULTIPOINTTYPE, 1, 1 }, { "MULTIPOINTZ", MULTIPOINTTYPE, 1, 0 }, { "MULTIPOINTM", MULTIPOINTTYPE, 0, 1 }, { "MULTIPOINT", MULTIPOINTTYPE, 0, 0 }, { "LINESTRINGZM", LINETYPE, 1, 1 }, { "LINESTRINGZ", LINETYPE, 1, 0 }, { "LINESTRINGM", LINETYPE, 0, 1 }, { "LINESTRING", LINETYPE, 0, 0 }, { "TRIANGLEZM", TRIANGLETYPE, 1, 1 }, { "TRIANGLEZ", TRIANGLETYPE, 1, 0 }, { "TRIANGLEM", TRIANGLETYPE, 0, 1 }, { "TRIANGLE", TRIANGLETYPE, 0, 0 }, { "POLYGONZM", POLYGONTYPE, 1, 1 }, { "POLYGONZ", POLYGONTYPE, 1, 0 }, { "POLYGONM", POLYGONTYPE, 0, 1 }, { "POLYGON", POLYGONTYPE, 0, 0 }, { "POINTZM", POINTTYPE, 1, 1 }, { "POINTZ", POINTTYPE, 1, 0 }, { "POINTM", POINTTYPE, 0, 1 }, { "POINT", POINTTYPE, 0, 0 } }; #define GEOMTYPE_STRUCT_ARRAY_LEN (sizeof geomtype_struct_array/sizeof(struct geomtype_struct)) /* * We use a very simple upper case mapper here, because the system toupper() function * is locale dependent and may have trouble mapping lower case strings to the upper * case ones we expect (see, the "Turkisk I", http://www.i18nguy.com/unicode/turkish-i18n.html) * We could also count on PgSQL sending us *lower* case inputs, as it seems to do that * regardless of the case the user provides for the type arguments. */ const char dumb_upper_map[128] = "................................................0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......ABCDEFGHIJKLMNOPQRSTUVWXYZ....."; static char dumb_toupper(int in) { if ( in < 0 || in > 127 ) return '.'; return dumb_upper_map[in]; } lwflags_t lwflags(int hasz, int hasm, int geodetic) { lwflags_t flags = 0; if (hasz) FLAGS_SET_Z(flags, 1); if (hasm) FLAGS_SET_M(flags, 1); if (geodetic) FLAGS_SET_GEODETIC(flags, 1); return flags; } /** * Calculate type integer and dimensional flags from string input. * Case insensitive, and insensitive to spaces at front and back. * Type == 0 in the case of the string "GEOMETRY" or "GEOGRAPHY". * Return LW_SUCCESS for success. */ int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m) { char *tmpstr; size_t tmpstartpos, tmpendpos; size_t i; assert(str); assert(type); assert(z); assert(m); /* Initialize. */ *type = 0; *z = 0; *m = 0; /* Locate any leading/trailing spaces */ tmpstartpos = 0; for (i = 0; i < strlen(str); i++) { if (str[i] != ' ') { tmpstartpos = i; break; } } tmpendpos = strlen(str) - 1; for (i = strlen(str) - 1; i != 0; i--) { if (str[i] != ' ') { tmpendpos = i; break; } } /* Copy and convert to upper case for comparison */ tmpstr = lwalloc(tmpendpos - tmpstartpos + 2); for (i = tmpstartpos; i <= tmpendpos; i++) tmpstr[i - tmpstartpos] = dumb_toupper(str[i]); /* Add NULL to terminate */ tmpstr[i - tmpstartpos] = '\0'; /* Now check for the type */ for (i = 0; i < GEOMTYPE_STRUCT_ARRAY_LEN; i++) { if (!strcmp(tmpstr, geomtype_struct_array[i].typename)) { *type = geomtype_struct_array[i].type; *z = geomtype_struct_array[i].z; *m = geomtype_struct_array[i].m; lwfree(tmpstr); return LW_SUCCESS; } } lwfree(tmpstr); return LW_FAILURE; } lwgeom/src/liblwgeom/varint.h0000644000176200001440000000365513773172540016012 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2014 Sandro Santilli * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #ifndef _LIBLWGEOM_VARINT_H #define _LIBLWGEOM_VARINT_H 1 #include #include /* NEW SIGNATURES */ size_t varint_u32_encode_buf(uint32_t val, uint8_t *buf); size_t varint_s32_encode_buf(int32_t val, uint8_t *buf); size_t varint_u64_encode_buf(uint64_t val, uint8_t *buf); size_t varint_s64_encode_buf(int64_t val, uint8_t *buf); int64_t varint_s64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); uint64_t varint_u64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); size_t varint_size(const uint8_t *the_start, const uint8_t *the_end); /* Support from -INT{8,32,64}_MAX to INT{8,32,64}_MAX), * e.g INT8_MIN is not supported in zigzag8 */ uint64_t zigzag64(int64_t val); uint32_t zigzag32(int32_t val); uint8_t zigzag8(int8_t val); int64_t unzigzag64(uint64_t val); int32_t unzigzag32(uint32_t val); int8_t unzigzag8(uint8_t val); #endif /* !defined _LIBLWGEOM_VARINT_H */ lwgeom/src/liblwgeom/lwin_wkt_parse.h0000644000176200001440000000750713773172540017537 0ustar liggesusers/* A Bison parser, made by GNU Bison 3.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Undocumented macros, especially those whose name start with YY_, are private implementation details. Do not rely on them. */ #ifndef YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED # define YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int wkt_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { POINT_TOK = 258, LINESTRING_TOK = 259, POLYGON_TOK = 260, MPOINT_TOK = 261, MLINESTRING_TOK = 262, MPOLYGON_TOK = 263, MSURFACE_TOK = 264, MCURVE_TOK = 265, CURVEPOLYGON_TOK = 266, COMPOUNDCURVE_TOK = 267, CIRCULARSTRING_TOK = 268, COLLECTION_TOK = 269, RBRACKET_TOK = 270, LBRACKET_TOK = 271, COMMA_TOK = 272, EMPTY_TOK = 273, SEMICOLON_TOK = 274, TRIANGLE_TOK = 275, TIN_TOK = 276, POLYHEDRALSURFACE_TOK = 277, DOUBLE_TOK = 278, DIMENSIONALITY_TOK = 279, SRID_TOK = 280 }; #endif /* Tokens. */ #define POINT_TOK 258 #define LINESTRING_TOK 259 #define POLYGON_TOK 260 #define MPOINT_TOK 261 #define MLINESTRING_TOK 262 #define MPOLYGON_TOK 263 #define MSURFACE_TOK 264 #define MCURVE_TOK 265 #define CURVEPOLYGON_TOK 266 #define COMPOUNDCURVE_TOK 267 #define CIRCULARSTRING_TOK 268 #define COLLECTION_TOK 269 #define RBRACKET_TOK 270 #define LBRACKET_TOK 271 #define COMMA_TOK 272 #define EMPTY_TOK 273 #define SEMICOLON_TOK 274 #define TRIANGLE_TOK 275 #define TIN_TOK 276 #define POLYHEDRALSURFACE_TOK 277 #define DOUBLE_TOK 278 #define DIMENSIONALITY_TOK 279 #define SRID_TOK 280 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 112 "lwin_wkt_parse.y" int integervalue; double doublevalue; char *stringvalue; LWGEOM *geometryvalue; POINT coordinatevalue; POINTARRAY *ptarrayvalue; #line 116 "lwin_wkt_parse.h" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE wkt_yylval; extern YYLTYPE wkt_yylloc; int wkt_yyparse (void); #endif /* !YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED */ lwgeom/src/liblwgeom/lwin_wkt_lex.c0000644000176200001440000016703214332733245017205 0ustar liggesusers#line 1 "lwin_wkt_lex.c" #line 3 "lwin_wkt_lex.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define yy_create_buffer wkt_yy_create_buffer #define yy_delete_buffer wkt_yy_delete_buffer #define yy_scan_buffer wkt_yy_scan_buffer #define yy_scan_string wkt_yy_scan_string #define yy_scan_bytes wkt_yy_scan_bytes #define yy_init_buffer wkt_yy_init_buffer #define yy_flush_buffer wkt_yy_flush_buffer #define yy_load_buffer_state wkt_yy_load_buffer_state #define yy_switch_to_buffer wkt_yy_switch_to_buffer #define yypush_buffer_state wkt_yypush_buffer_state #define yypop_buffer_state wkt_yypop_buffer_state #define yyensure_buffer_stack wkt_yyensure_buffer_stack #define yy_flex_debug wkt_yy_flex_debug #define yyin wkt_yyin #define yyleng wkt_yyleng #define yylex wkt_yylex #define yylineno wkt_yylineno #define yyout wkt_yyout #define yyrestart wkt_yyrestart #define yytext wkt_yytext #define yywrap wkt_yywrap #define yyalloc wkt_yyalloc #define yyrealloc wkt_yyrealloc #define yyfree wkt_yyfree #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yy_create_buffer #define wkt_yy_create_buffer_ALREADY_DEFINED #else #define yy_create_buffer wkt_yy_create_buffer #endif #ifdef yy_delete_buffer #define wkt_yy_delete_buffer_ALREADY_DEFINED #else #define yy_delete_buffer wkt_yy_delete_buffer #endif #ifdef yy_scan_buffer #define wkt_yy_scan_buffer_ALREADY_DEFINED #else #define yy_scan_buffer wkt_yy_scan_buffer #endif #ifdef yy_scan_string #define wkt_yy_scan_string_ALREADY_DEFINED #else #define yy_scan_string wkt_yy_scan_string #endif #ifdef yy_scan_bytes #define wkt_yy_scan_bytes_ALREADY_DEFINED #else #define yy_scan_bytes wkt_yy_scan_bytes #endif #ifdef yy_init_buffer #define wkt_yy_init_buffer_ALREADY_DEFINED #else #define yy_init_buffer wkt_yy_init_buffer #endif #ifdef yy_flush_buffer #define wkt_yy_flush_buffer_ALREADY_DEFINED #else #define yy_flush_buffer wkt_yy_flush_buffer #endif #ifdef yy_load_buffer_state #define wkt_yy_load_buffer_state_ALREADY_DEFINED #else #define yy_load_buffer_state wkt_yy_load_buffer_state #endif #ifdef yy_switch_to_buffer #define wkt_yy_switch_to_buffer_ALREADY_DEFINED #else #define yy_switch_to_buffer wkt_yy_switch_to_buffer #endif #ifdef yypush_buffer_state #define wkt_yypush_buffer_state_ALREADY_DEFINED #else #define yypush_buffer_state wkt_yypush_buffer_state #endif #ifdef yypop_buffer_state #define wkt_yypop_buffer_state_ALREADY_DEFINED #else #define yypop_buffer_state wkt_yypop_buffer_state #endif #ifdef yyensure_buffer_stack #define wkt_yyensure_buffer_stack_ALREADY_DEFINED #else #define yyensure_buffer_stack wkt_yyensure_buffer_stack #endif #ifdef yylex #define wkt_yylex_ALREADY_DEFINED #else #define yylex wkt_yylex #endif #ifdef yyrestart #define wkt_yyrestart_ALREADY_DEFINED #else #define yyrestart wkt_yyrestart #endif #ifdef yylex_init #define wkt_yylex_init_ALREADY_DEFINED #else #define yylex_init wkt_yylex_init #endif #ifdef yylex_init_extra #define wkt_yylex_init_extra_ALREADY_DEFINED #else #define yylex_init_extra wkt_yylex_init_extra #endif #ifdef yylex_destroy #define wkt_yylex_destroy_ALREADY_DEFINED #else #define yylex_destroy wkt_yylex_destroy #endif #ifdef yyget_debug #define wkt_yyget_debug_ALREADY_DEFINED #else #define yyget_debug wkt_yyget_debug #endif #ifdef yyset_debug #define wkt_yyset_debug_ALREADY_DEFINED #else #define yyset_debug wkt_yyset_debug #endif #ifdef yyget_extra #define wkt_yyget_extra_ALREADY_DEFINED #else #define yyget_extra wkt_yyget_extra #endif #ifdef yyset_extra #define wkt_yyset_extra_ALREADY_DEFINED #else #define yyset_extra wkt_yyset_extra #endif #ifdef yyget_in #define wkt_yyget_in_ALREADY_DEFINED #else #define yyget_in wkt_yyget_in #endif #ifdef yyset_in #define wkt_yyset_in_ALREADY_DEFINED #else #define yyset_in wkt_yyset_in #endif #ifdef yyget_out #define wkt_yyget_out_ALREADY_DEFINED #else #define yyget_out wkt_yyget_out #endif #ifdef yyset_out #define wkt_yyset_out_ALREADY_DEFINED #else #define yyset_out wkt_yyset_out #endif #ifdef yyget_leng #define wkt_yyget_leng_ALREADY_DEFINED #else #define yyget_leng wkt_yyget_leng #endif #ifdef yyget_text #define wkt_yyget_text_ALREADY_DEFINED #else #define yyget_text wkt_yyget_text #endif #ifdef yyget_lineno #define wkt_yyget_lineno_ALREADY_DEFINED #else #define yyget_lineno wkt_yyget_lineno #endif #ifdef yyset_lineno #define wkt_yyset_lineno_ALREADY_DEFINED #else #define yyset_lineno wkt_yyset_lineno #endif #ifdef yywrap #define wkt_yywrap_ALREADY_DEFINED #else #define yywrap wkt_yywrap #endif #ifdef yyalloc #define wkt_yyalloc_ALREADY_DEFINED #else #define yyalloc wkt_yyalloc #endif #ifdef yyrealloc #define wkt_yyrealloc_ALREADY_DEFINED #else #define yyrealloc wkt_yyrealloc #endif #ifdef yyfree #define wkt_yyfree_ALREADY_DEFINED #else #define yyfree wkt_yyfree #endif #ifdef yytext #define wkt_yytext_ALREADY_DEFINED #else #define yytext wkt_yytext #endif #ifdef yyleng #define wkt_yyleng_ALREADY_DEFINED #else #define yyleng wkt_yyleng #endif #ifdef yyin #define wkt_yyin_ALREADY_DEFINED #else #define yyin wkt_yyin #endif #ifdef yyout #define wkt_yyout_ALREADY_DEFINED #else #define yyout wkt_yyout #endif #ifdef yy_flex_debug #define wkt_yy_flex_debug_ALREADY_DEFINED #else #define yy_flex_debug wkt_yy_flex_debug #endif #ifdef yylineno #define wkt_yylineno_ALREADY_DEFINED #else #define yylineno wkt_yylineno #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include #include "R.h" /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an * integer in range [0..255] for use as an array index. */ #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = NULL; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart ( FILE *input_file ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); void yy_delete_buffer ( YY_BUFFER_STATE b ); void yy_flush_buffer ( YY_BUFFER_STATE b ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); void yypop_buffer_state ( void ); static void yyensure_buffer_stack ( void ); static void yy_load_buffer_state ( void ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); void *yyalloc ( yy_size_t ); void *yyrealloc ( void *, yy_size_t ); void yyfree ( void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define wkt_yywrap() (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; FILE *yyin = NULL, *yyout = NULL; typedef int yy_state_type; extern int yylineno; int yylineno = 1; extern char *yytext; #ifdef yytext_ptr #undef yytext_ptr #endif #define yytext_ptr yytext static yy_state_type yy_get_previous_state ( void ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); static int yy_get_next_buffer ( void ); static void yynoreturn yy_fatal_error ( const char* msg ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 26 #define YY_END_OF_BUFFER 27 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[173] = { 0, 0, 0, 27, 25, 24, 24, 20, 21, 22, 25, 25, 25, 23, 25, 25, 25, 25, 19, 25, 25, 25, 19, 24, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 5, 4, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 0, 7, 0, 0, 0, 0, 14, 3, 0 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 6, 7, 8, 9, 1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1, 11, 1, 12, 1, 1, 1, 13, 1, 14, 15, 16, 17, 18, 19, 20, 1, 1, 21, 22, 23, 24, 25, 1, 26, 27, 28, 29, 30, 1, 1, 31, 32, 1, 1, 1, 1, 1, 1, 33, 1, 34, 35, 36, 37, 38, 39, 40, 1, 1, 41, 42, 43, 44, 45, 1, 46, 47, 48, 49, 50, 1, 1, 51, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[53] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const flex_int16_t yy_base[173] = { 0, 0, 0, 202, 384, 51, 53, 384, 384, 384, 48, 190, 57, 384, 41, 41, 52, 49, 42, 48, 48, 56, 53, 75, 186, 0, 96, 384, 106, 109, 54, 62, 61, 80, 86, 91, 97, 100, 102, 100, 104, 384, 179, 126, 112, 102, 105, 109, 117, 129, 120, 126, 119, 136, 384, 139, 124, 130, 140, 127, 144, 134, 142, 135, 148, 85, 141, 154, 148, 154, 384, 157, 160, 195, 384, 166, 175, 184, 175, 185, 178, 179, 178, 180, 178, 192, 190, 186, 194, 204, 76, 69, 204, 202, 215, 210, 202, 218, 215, 220, 226, 218, 384, 223, 236, 227, 241, 225, 243, 236, 230, 246, 242, 237, 253, 258, 384, 245, 249, 263, 259, 266, 270, 260, 261, 274, 281, 274, 270, 271, 275, 279, 384, 384, 274, 384, 281, 294, 283, 291, 283, 295, 300, 297, 301, 310, 298, 306, 316, 384, 318, 315, 384, 384, 310, 319, 384, 325, 317, 325, 384, 317, 330, 337, 331, 384, 339, 334, 345, 340, 384, 384, 384 } ; static const flex_int16_t yy_def[173] = { 0, 172, 1, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 12, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 0 } ; static const flex_int16_t yy_nxt[437] = { 0, 4, 5, 6, 7, 8, 4, 9, 10, 11, 12, 13, 4, 4, 14, 4, 15, 4, 16, 4, 4, 17, 18, 4, 4, 19, 4, 20, 21, 4, 4, 4, 22, 4, 14, 4, 15, 4, 16, 4, 4, 17, 18, 4, 4, 19, 4, 20, 21, 4, 4, 4, 22, 23, 23, 23, 23, 24, 25, 27, 27, 30, 27, 33, 27, 31, 28, 25, 34, 35, 32, 36, 37, 29, 38, 41, 39, 23, 23, 91, 44, 30, 40, 33, 45, 31, 91, 46, 34, 35, 32, 36, 37, 29, 38, 41, 39, 77, 27, 27, 44, 27, 40, 27, 45, 47, 26, 46, 27, 27, 48, 27, 29, 27, 49, 42, 26, 42, 50, 43, 51, 52, 53, 54, 55, 47, 56, 57, 27, 27, 48, 27, 29, 27, 49, 58, 43, 59, 50, 60, 51, 52, 53, 54, 55, 61, 56, 57, 62, 63, 64, 65, 66, 67, 68, 58, 69, 59, 70, 60, 71, 72, 73, 74, 78, 61, 75, 76, 62, 63, 64, 65, 66, 67, 68, 79, 69, 80, 70, 81, 71, 72, 73, 74, 78, 82, 75, 76, 83, 43, 88, 89, 90, 92, 91, 79, 26, 80, 93, 81, 26, 94, 172, 95, 96, 82, 97, 98, 83, 84, 88, 89, 99, 92, 100, 101, 85, 102, 93, 103, 86, 94, 87, 95, 96, 104, 97, 98, 105, 84, 106, 107, 99, 108, 100, 101, 85, 102, 109, 103, 86, 110, 87, 111, 114, 104, 112, 113, 105, 115, 106, 107, 116, 108, 117, 118, 119, 120, 109, 121, 122, 110, 123, 111, 114, 124, 112, 113, 125, 115, 126, 127, 116, 128, 117, 118, 119, 120, 129, 121, 122, 130, 123, 131, 132, 124, 133, 134, 125, 135, 126, 127, 136, 128, 137, 138, 139, 140, 129, 141, 142, 130, 143, 131, 132, 144, 133, 134, 145, 135, 146, 147, 136, 148, 137, 138, 139, 140, 149, 141, 142, 150, 143, 151, 152, 144, 153, 154, 145, 155, 146, 147, 156, 148, 157, 158, 159, 160, 149, 161, 162, 150, 163, 151, 152, 164, 153, 154, 165, 155, 166, 167, 156, 168, 157, 158, 159, 160, 169, 161, 162, 170, 163, 171, 172, 164, 172, 172, 165, 172, 166, 167, 172, 168, 172, 172, 172, 172, 169, 172, 172, 170, 172, 171, 3, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172 } ; static const flex_int16_t yy_chk[437] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 6, 6, 10, 10, 12, 12, 14, 12, 15, 12, 14, 12, 12, 16, 17, 14, 18, 19, 12, 20, 22, 21, 23, 23, 91, 30, 14, 21, 15, 31, 14, 90, 32, 16, 17, 14, 18, 19, 12, 20, 22, 21, 65, 26, 26, 30, 26, 21, 26, 31, 33, 26, 32, 28, 28, 34, 28, 26, 28, 35, 29, 28, 29, 36, 29, 37, 37, 38, 39, 40, 33, 44, 45, 43, 43, 34, 43, 26, 43, 35, 46, 43, 47, 36, 48, 37, 37, 38, 39, 40, 49, 44, 45, 50, 51, 52, 53, 55, 56, 57, 46, 58, 47, 59, 48, 60, 61, 62, 63, 66, 49, 64, 64, 50, 51, 52, 53, 55, 56, 57, 67, 58, 68, 59, 69, 60, 61, 62, 63, 66, 71, 64, 64, 72, 42, 75, 76, 77, 78, 77, 67, 24, 68, 79, 69, 11, 80, 3, 81, 82, 71, 83, 84, 72, 73, 75, 76, 85, 78, 86, 87, 73, 88, 79, 89, 73, 80, 73, 81, 82, 92, 83, 84, 93, 73, 94, 95, 85, 96, 86, 87, 73, 88, 97, 89, 73, 98, 73, 99, 101, 92, 100, 100, 93, 103, 94, 95, 104, 96, 105, 106, 107, 108, 97, 109, 110, 98, 111, 99, 101, 112, 100, 100, 113, 103, 114, 115, 104, 117, 105, 106, 107, 108, 118, 109, 110, 119, 111, 120, 121, 112, 122, 123, 113, 124, 114, 115, 125, 117, 126, 127, 128, 129, 118, 130, 131, 119, 134, 120, 121, 136, 122, 123, 137, 124, 138, 139, 125, 140, 126, 127, 128, 129, 141, 130, 131, 142, 134, 143, 144, 136, 145, 146, 137, 147, 138, 139, 148, 140, 150, 151, 154, 155, 141, 157, 158, 142, 159, 143, 144, 161, 145, 146, 162, 147, 163, 164, 148, 166, 150, 151, 154, 155, 167, 157, 158, 168, 159, 169, 0, 161, 0, 0, 162, 0, 163, 164, 0, 166, 0, 0, 0, 0, 167, 0, 0, 168, 0, 169, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_flex_debug; int yy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yytext; #line 1 "lwin_wkt_lex.l" #line 2 "lwin_wkt_lex.l" /* The lexer */ #include #include #include "lwin_wkt.h" #include "lwin_wkt_parse.h" #include "lwgeom_log.h" static YY_BUFFER_STATE wkt_yy_buf_state; /* * Handle errors due to unexpected junk in WKT strings. */ static void wkt_lexer_unknown(void) { /* Set the global error state */ global_parser_result.errcode = PARSER_ERROR_OTHER; global_parser_result.message = parser_error_messages[PARSER_ERROR_OTHER]; global_parser_result.errlocation = wkt_yylloc.last_column; } /* * This macro is magically run after a rule is found but before the main * action is run. We use it to update the parse location information * so we can report on where things fail. Also optionally to dump * debugging info. */ #define YY_USER_ACTION do { \ wkt_yylloc.first_line = wkt_yylloc.last_line = yylineno; \ wkt_yylloc.first_column = wkt_yylloc.last_column; \ wkt_yylloc.last_column += yyleng; \ LWDEBUGF(5,"lex: %s", wkt_yytext); \ } while (0); #line 893 "lwin_wkt_lex.c" #define YY_NO_INPUT 1 /* Suppress the default implementations. */ #line 896 "lwin_wkt_lex.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals ( void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( void ); int yyget_debug ( void ); void yyset_debug ( int debug_flag ); YY_EXTRA_TYPE yyget_extra ( void ); void yyset_extra ( YY_EXTRA_TYPE user_defined ); FILE *yyget_in ( void ); void yyset_in ( FILE * _in_str ); FILE *yyget_out ( void ); void yyset_out ( FILE * _out_str ); int yyget_leng ( void ); char *yyget_text ( void ); int yyget_lineno ( void ); void yyset_lineno ( int _line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( void ); #else extern int yywrap ( void ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput ( void ); #else static int input ( void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex (void); #define YY_DECL int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; /* if ( ! yyout ) yyout = stdout; */ if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE ); } yy_load_buffer_state( ); } { #line 49 "lwin_wkt_lex.l" #line 1114 "lwin_wkt_lex.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 173 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 172 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 51 "lwin_wkt_lex.l" { LWDEBUG(5,"DOUBLE"); wkt_yylval.doublevalue = atof(wkt_yytext); yyless(wkt_yyleng-1); return DOUBLE_TOK; } YY_BREAK case 2: YY_RULE_SETUP #line 58 "lwin_wkt_lex.l" { LWDEBUG(5,"SRID"); wkt_yylval.integervalue = wkt_lexer_read_srid(wkt_yytext); return SRID_TOK; } YY_BREAK case 3: YY_RULE_SETUP #line 64 "lwin_wkt_lex.l" { return COLLECTION_TOK; } YY_BREAK case 4: YY_RULE_SETUP #line 65 "lwin_wkt_lex.l" { return MSURFACE_TOK; } YY_BREAK case 5: YY_RULE_SETUP #line 66 "lwin_wkt_lex.l" { return MPOLYGON_TOK; } YY_BREAK case 6: YY_RULE_SETUP #line 67 "lwin_wkt_lex.l" { return MCURVE_TOK; } YY_BREAK case 7: YY_RULE_SETUP #line 68 "lwin_wkt_lex.l" { return MLINESTRING_TOK; } YY_BREAK case 8: YY_RULE_SETUP #line 69 "lwin_wkt_lex.l" { return MPOINT_TOK; } YY_BREAK case 9: YY_RULE_SETUP #line 70 "lwin_wkt_lex.l" { return CURVEPOLYGON_TOK; } YY_BREAK case 10: YY_RULE_SETUP #line 71 "lwin_wkt_lex.l" { return POLYGON_TOK; } YY_BREAK case 11: YY_RULE_SETUP #line 72 "lwin_wkt_lex.l" { return COMPOUNDCURVE_TOK; } YY_BREAK case 12: YY_RULE_SETUP #line 73 "lwin_wkt_lex.l" { return CIRCULARSTRING_TOK; } YY_BREAK case 13: YY_RULE_SETUP #line 74 "lwin_wkt_lex.l" { return LINESTRING_TOK; } YY_BREAK case 14: YY_RULE_SETUP #line 75 "lwin_wkt_lex.l" { return POLYHEDRALSURFACE_TOK; } YY_BREAK case 15: YY_RULE_SETUP #line 76 "lwin_wkt_lex.l" { return TRIANGLE_TOK; } YY_BREAK case 16: YY_RULE_SETUP #line 77 "lwin_wkt_lex.l" { return TIN_TOK; } YY_BREAK case 17: YY_RULE_SETUP #line 78 "lwin_wkt_lex.l" { return POINT_TOK; } YY_BREAK case 18: YY_RULE_SETUP #line 79 "lwin_wkt_lex.l" { return EMPTY_TOK; } YY_BREAK case 19: YY_RULE_SETUP #line 81 "lwin_wkt_lex.l" { LWDEBUG(5,"DIMENSIONALITY"); wkt_yylval.stringvalue = wkt_yytext; return DIMENSIONALITY_TOK; } YY_BREAK case 20: YY_RULE_SETUP #line 87 "lwin_wkt_lex.l" { LWDEBUG(5,"LBRACKET"); return LBRACKET_TOK; } YY_BREAK case 21: YY_RULE_SETUP #line 88 "lwin_wkt_lex.l" { LWDEBUG(5,"RBRACKET"); return RBRACKET_TOK; } YY_BREAK case 22: YY_RULE_SETUP #line 89 "lwin_wkt_lex.l" { LWDEBUG(5,"COMMA"); return COMMA_TOK; } YY_BREAK case 23: YY_RULE_SETUP #line 90 "lwin_wkt_lex.l" { LWDEBUG(5,"SEMICOLON"); return SEMICOLON_TOK; } YY_BREAK case 24: /* rule 24 can match eol */ YY_RULE_SETUP #line 92 "lwin_wkt_lex.l" { /* ignore whitespace */ LWDEBUG(5,"WHITESPACE"); } YY_BREAK case 25: YY_RULE_SETUP #line 94 "lwin_wkt_lex.l" { /* Error out and stop parsing on unknown/unexpected characters */ LWDEBUG(5,"UNKNOWN"); wkt_lexer_unknown(); yyterminate(); } YY_BREAK case 26: YY_RULE_SETUP #line 100 "lwin_wkt_lex.l" ECHO; YY_BREAK #line 1316 "lwin_wkt_lex.c" case YY_STATE_EOF(INITIAL): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = (yytext_ptr); int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc( (void *) b->yy_ch_buf, (yy_size_t) (b->yy_buf_size + 2) ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); /* "- 2" to take care of EOB's */ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { yy_state_type yy_current_state; char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 173 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { int yy_is_jam; char *yy_cp = (yy_c_buf_p); YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 173 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 172); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return 0; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE ); } yy_init_buffer( YY_CURRENT_BUFFER, input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf ); yyfree( (void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer( b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { yy_size_t num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr ) { return yy_scan_bytes( yystr, (int) strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg ) { /* fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); */ error("%s\n", msg); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ int yyget_leng (void) { return yyleng; } /** Get the current token. * */ char *yyget_text (void) { return yytext; } /** Set the current line number. * @param _line_number line number * */ void yyset_lineno (int _line_number ) { yylineno = _line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str ) { yyin = _in_str ; } void yyset_out (FILE * _out_str ) { yyout = _out_str ; } int yyget_debug (void) { return yy_flex_debug; } void yyset_debug (int _bdebug ) { yy_flex_debug = _bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ (yy_buffer_stack) = NULL; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = NULL; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; // yyout = stdout; yyout = NULL; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer( YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n ) { int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (const char * s ) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif #define YYTABLES_NAME "yytables" #line 100 "lwin_wkt_lex.l" void *wkt_yyalloc (yy_size_t size ) { return (void *) lwalloc( size ); } void *wkt_yyrealloc (void * ptr, yy_size_t size ) { return (void *) lwrealloc( (char *) ptr, size ); } void wkt_yyfree (void * ptr ) { lwfree( (char *) ptr ); /* see wkt_yyrealloc() for (char *) cast */ } /* * Set up the lexer! */ void wkt_lexer_init(char *src) { yy_init_globals(); wkt_yy_buf_state = wkt_yy_scan_string(src); } /* * Clean up the lexer! */ void wkt_lexer_close(void) { wkt_yy_delete_buffer(wkt_yy_buf_state); } lwgeom/src/liblwgeom/lwgeom_median.c0000644000176200001440000001723413773172540017307 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * Copyright 2017 Darafei Praliaskouski * **********************************************************************/ #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" static double calc_weighted_distances_3d(const POINT3D* curr, const POINT4D* points, uint32_t npoints, double* distances) { uint32_t i; double weight = 0.0; for (i = 0; i < npoints; i++) { double dist = distance3d_pt_pt(curr, (POINT3D*)&points[i]); distances[i] = dist / points[i].m; weight += dist * points[i].m; } return weight; } static uint32_t iterate_4d(POINT3D* curr, const POINT4D* points, const uint32_t npoints, const uint32_t max_iter, const double tol) { uint32_t i, iter; double delta; double sum_curr = 0, sum_next = 0; int hit = LW_FALSE; double *distances = lwalloc(npoints * sizeof(double)); sum_curr = calc_weighted_distances_3d(curr, points, npoints, distances); for (iter = 0; iter < max_iter; iter++) { POINT3D next = { 0, 0, 0 }; double denom = 0; /** Calculate denom to get the next point */ for (i = 0; i < npoints; i++) { /* we need to use lower epsilon than in FP_IS_ZERO in the loop for calculation to converge */ if (distances[i] > DBL_EPSILON) { next.x += points[i].x / distances[i]; next.y += points[i].y / distances[i]; next.z += points[i].z / distances[i]; denom += 1.0 / distances[i]; } else { hit = LW_TRUE; } } if (denom < DBL_EPSILON) { /* No movement - Final point */ break; } /* Calculate the new point */ next.x /= denom; next.y /= denom; next.z /= denom; /* If any of the intermediate points in the calculation is found in the * set of input points, the standard Weiszfeld method gets stuck with a * divide-by-zero. * * To get ourselves out of the hole, we follow an alternate procedure to * get the next iteration, as described in: * * Vardi, Y. and Zhang, C. (2011) "A modified Weiszfeld algorithm for the * Fermat-Weber location problem." Math. Program., Ser. A 90: 559-566. * DOI 10.1007/s101070100222 * * Available online at the time of this writing at * http://www.stat.rutgers.edu/home/cunhui/papers/43.pdf */ if (hit) { double dx = 0, dy = 0, dz = 0; double d_sqr; hit = LW_FALSE; for (i = 0; i < npoints; i++) { if (distances[i] > DBL_EPSILON) { dx += (points[i].x - curr->x) / distances[i]; dy += (points[i].y - curr->y) / distances[i]; dz += (points[i].z - curr->z) / distances[i]; } } d_sqr = sqrt(dx*dx + dy*dy + dz*dz); if (d_sqr > DBL_EPSILON) { double r_inv = FP_MAX(0, 1.0 / d_sqr); next.x = (1.0 - r_inv)*next.x + r_inv*curr->x; next.y = (1.0 - r_inv)*next.y + r_inv*curr->y; next.z = (1.0 - r_inv)*next.z + r_inv*curr->z; } } /* Check movement with next point */ sum_next = calc_weighted_distances_3d(&next, points, npoints, distances); delta = sum_curr - sum_next; if (delta < tol) { break; } else { curr->x = next.x; curr->y = next.y; curr->z = next.z; sum_curr = sum_next; } } lwfree(distances); return iter; } static POINT3D init_guess(const POINT4D* points, uint32_t npoints) { assert(npoints > 0); POINT3D guess = { 0, 0, 0 }; double mass = 0; uint32_t i; for (i = 0; i < npoints; i++) { guess.x += points[i].x * points[i].m; guess.y += points[i].y * points[i].m; guess.z += points[i].z * points[i].m; mass += points[i].m; } guess.x /= mass; guess.y /= mass; guess.z /= mass; return guess; } POINT4D* lwmpoint_extract_points_4d(const LWMPOINT* g, uint32_t* npoints, int* input_empty) { uint32_t i; uint32_t n = 0; POINT4D* points = lwalloc(g->ngeoms * sizeof(POINT4D)); int has_m = lwgeom_has_m((LWGEOM*) g); for (i = 0; i < g->ngeoms; i++) { LWGEOM* subg = lwcollection_getsubgeom((LWCOLLECTION*) g, i); if (!lwgeom_is_empty(subg)) { *input_empty = LW_FALSE; if (!getPoint4d_p(((LWPOINT*) subg)->point, 0, &points[n])) { lwerror("Geometric median: getPoint4d_p reported failure on point (POINT(%g %g %g %g), number %d of %d in input).", points[n].x, points[n].y, points[n].z, points[n].m, i, g->ngeoms); lwfree(points); return NULL; } if (has_m) { /* This limitation on non-negativity can be lifted * by replacing Weiszfeld algorithm with different one. * Possible option described in: * * Drezner, Zvi & O. Wesolowsky, George. (1991). * The Weber Problem On The Plane With Some Negative Weights. * INFOR. Information Systems and Operational Research. * 29. 10.1080/03155986.1991.11732158. */ if (points[n].m < 0) { lwerror("Geometric median input contains points with negative weights (POINT(%g %g %g %g), number %d of %d in input). Implementation can't guarantee global minimum convergence.", points[n].x, points[n].y, points[n].z, points[n].m, i, g->ngeoms); lwfree(points); return NULL; } /* points with zero weight are not going to affect calculation, drop them early */ if (points[n].m > DBL_EPSILON) n++; } else { points[n].m = 1.0; n++; } } } #if PARANOIA_LEVEL > 0 /* check Z=0 for 2D inputs*/ if (!lwgeom_has_z((LWGEOM*) g)) for (i = 0; i < n; i++) assert(points[i].z == 0); #endif *npoints = n; return points; } LWPOINT* lwmpoint_median(const LWMPOINT* g, double tol, uint32_t max_iter, char fail_if_not_converged) { /* m ordinate is considered weight, if defined */ uint32_t npoints = 0; /* we need to count this ourselves so we can exclude empties and weightless points */ uint32_t i; int input_empty = LW_TRUE; POINT3D median; POINT4D* points = lwmpoint_extract_points_4d(g, &npoints, &input_empty); /* input validation failed, error reported already */ if (points == NULL) return NULL; if (npoints == 0) { lwfree(points); if (input_empty) { return lwpoint_construct_empty(g->srid, 0, 0); } else { lwerror("Median failed to find non-empty input points with positive weight."); return NULL; } } median = init_guess(points, npoints); i = iterate_4d(&median, points, npoints, max_iter, tol); lwfree(points); if (fail_if_not_converged && i >= max_iter) { lwerror("Median failed to converge within %g after %d iterations.", tol, max_iter); return NULL; } if (lwgeom_has_z((LWGEOM*) g)) { return lwpoint_make3dz(g->srid, median.x, median.y, median.z); } else { return lwpoint_make2d(g->srid, median.x, median.y); } } LWPOINT* lwgeom_median(const LWGEOM* g, double tol, uint32_t max_iter, char fail_if_not_converged) { switch (g->type) { case POINTTYPE: return lwpoint_clone(lwgeom_as_lwpoint(g)); case MULTIPOINTTYPE: return lwmpoint_median(lwgeom_as_lwmpoint(g), tol, max_iter, fail_if_not_converged); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type)); return NULL; } } lwgeom/src/liblwgeom/lwgeom_geos_clean.c0000644000176200001440000005360313773172540020151 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009-2010 Sandro Santilli * **********************************************************************/ #include "liblwgeom.h" #include "lwgeom_geos.h" #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include #include #include /* #define POSTGIS_DEBUG_LEVEL 4 */ /* #define PARANOIA_LEVEL 2 */ #undef LWGEOM_PROFILE_MAKEVALID #if POSTGIS_GEOS_VERSION < 38 /* * Return Nth vertex in GEOSGeometry as a POINT. * May return NULL if the geometry has NO vertex. */ GEOSGeometry* LWGEOM_GEOS_getPointN(const GEOSGeometry*, uint32_t); GEOSGeometry* LWGEOM_GEOS_getPointN(const GEOSGeometry* g_in, uint32_t n) { uint32_t dims = 0; const GEOSCoordSequence* seq_in; GEOSCoordSeq seq_out; double val; uint32_t sz = 0; int gn; GEOSGeometry* ret; switch (GEOSGeomTypeId(g_in)) { case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_GEOMETRYCOLLECTION: { for (gn = 0; gn < GEOSGetNumGeometries(g_in); ++gn) { const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn); ret = LWGEOM_GEOS_getPointN(g, n); if (ret) return ret; } break; } case GEOS_POLYGON: { ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n); if (ret) return ret; for (gn = 0; gn < GEOSGetNumInteriorRings(g_in); ++gn) { const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn); ret = LWGEOM_GEOS_getPointN(g, n); if (ret) return ret; } break; } case GEOS_POINT: case GEOS_LINESTRING: case GEOS_LINEARRING: break; } seq_in = GEOSGeom_getCoordSeq(g_in); if (!seq_in) return NULL; if (!GEOSCoordSeq_getSize(seq_in, &sz)) return NULL; if (!sz) return NULL; if (!GEOSCoordSeq_getDimensions(seq_in, &dims)) return NULL; seq_out = GEOSCoordSeq_create(1, dims); if (!seq_out) return NULL; if (!GEOSCoordSeq_getX(seq_in, n, &val)) return NULL; if (!GEOSCoordSeq_setX(seq_out, n, val)) return NULL; if (!GEOSCoordSeq_getY(seq_in, n, &val)) return NULL; if (!GEOSCoordSeq_setY(seq_out, n, val)) return NULL; if (dims > 2) { if (!GEOSCoordSeq_getZ(seq_in, n, &val)) return NULL; if (!GEOSCoordSeq_setZ(seq_out, n, val)) return NULL; } return GEOSGeom_createPoint(seq_out); } #endif LWGEOM* lwcollection_make_geos_friendly(LWCOLLECTION* g); LWGEOM* lwline_make_geos_friendly(LWLINE* line); LWGEOM* lwpoly_make_geos_friendly(LWPOLY* poly); POINTARRAY* ring_make_geos_friendly(POINTARRAY* ring); /* * Ensure the geometry is "structurally" valid * (enough for GEOS to accept it) * May return the input untouched (if already valid). * May return geometries of lower dimension (on collapses) */ static LWGEOM* lwgeom_make_geos_friendly(LWGEOM* geom) { LWDEBUGF(2, "lwgeom_make_geos_friendly enter (type %d)", geom->type); switch (geom->type) { case POINTTYPE: case MULTIPOINTTYPE: /* a point is always valid */ return geom; break; case LINETYPE: /* lines need at least 2 points */ return lwline_make_geos_friendly((LWLINE*)geom); break; case POLYGONTYPE: /* polygons need all rings closed and with npoints > 3 */ return lwpoly_make_geos_friendly((LWPOLY*)geom); break; case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return lwcollection_make_geos_friendly((LWCOLLECTION*)geom); break; case CIRCSTRINGTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTISURFACETYPE: case MULTICURVETYPE: default: lwerror("lwgeom_make_geos_friendly: unsupported input geometry type: %s (%d)", lwtype_name(geom->type), geom->type); break; } return 0; } /* * Close the point array, if not already closed in 2d. * Returns the input if already closed in 2d, or a newly * constructed POINTARRAY. * TODO: move in ptarray.c */ POINTARRAY* ptarray_close2d(POINTARRAY* ring); POINTARRAY* ptarray_close2d(POINTARRAY* ring) { POINTARRAY* newring; /* close the ring if not already closed (2d only) */ if (!ptarray_is_closed_2d(ring)) { /* close it up */ newring = ptarray_addPoint(ring, getPoint_internal(ring, 0), FLAGS_NDIMS(ring->flags), ring->npoints); ring = newring; } return ring; } /* May return the same input or a new one (never zero) */ POINTARRAY* ring_make_geos_friendly(POINTARRAY* ring) { POINTARRAY* closedring; POINTARRAY* ring_in = ring; /* close the ring if not already closed (2d only) */ closedring = ptarray_close2d(ring); if (closedring != ring) ring = closedring; /* return 0 for collapsed ring (after closeup) */ while (ring->npoints < 4) { POINTARRAY* oring = ring; LWDEBUGF(4, "ring has %d points, adding another", ring->npoints); /* let's add another... */ ring = ptarray_addPoint(ring, getPoint_internal(ring, 0), FLAGS_NDIMS(ring->flags), ring->npoints); if (oring != ring_in) ptarray_free(oring); } return ring; } /* Make sure all rings are closed and have > 3 points. * May return the input untouched. */ LWGEOM* lwpoly_make_geos_friendly(LWPOLY* poly) { LWGEOM* ret; POINTARRAY** new_rings; uint32_t i; /* If the polygon has no rings there's nothing to do */ if (!poly->nrings) return (LWGEOM*)poly; /* Allocate enough pointers for all rings */ new_rings = lwalloc(sizeof(POINTARRAY*) * poly->nrings); /* All rings must be closed and have > 3 points */ for (i = 0; i < poly->nrings; i++) { POINTARRAY* ring_in = poly->rings[i]; POINTARRAY* ring_out = ring_make_geos_friendly(ring_in); if (ring_in != ring_out) { LWDEBUGF( 3, "lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->npoints); ptarray_free(ring_in); } else LWDEBUGF(3, "lwpoly_make_geos_friendly: ring %d untouched", i); assert(ring_out); new_rings[i] = ring_out; } lwfree(poly->rings); poly->rings = new_rings; ret = (LWGEOM*)poly; return ret; } /* Need NO or >1 points. Duplicate first if only one. */ LWGEOM* lwline_make_geos_friendly(LWLINE* line) { LWGEOM* ret; if (line->points->npoints == 1) /* 0 is fine, 2 is fine */ { #if 1 /* Duplicate point */ line->points = ptarray_addPoint(line->points, getPoint_internal(line->points, 0), FLAGS_NDIMS(line->points->flags), line->points->npoints); ret = (LWGEOM*)line; #else /* Turn into a point */ ret = (LWGEOM*)lwpoint_construct(line->srid, 0, line->points); #endif return ret; } else { return (LWGEOM*)line; /* return lwline_clone(line); */ } } LWGEOM* lwcollection_make_geos_friendly(LWCOLLECTION* g) { LWGEOM** new_geoms; uint32_t i, new_ngeoms = 0; LWCOLLECTION* ret; /* enough space for all components */ new_geoms = lwalloc(sizeof(LWGEOM*) * g->ngeoms); ret = lwalloc(sizeof(LWCOLLECTION)); memcpy(ret, g, sizeof(LWCOLLECTION)); ret->maxgeoms = g->ngeoms; for (i = 0; i < g->ngeoms; i++) { LWGEOM* newg = lwgeom_make_geos_friendly(g->geoms[i]); if (newg) new_geoms[new_ngeoms++] = newg; } ret->bbox = NULL; /* recompute later... */ ret->ngeoms = new_ngeoms; if (new_ngeoms) ret->geoms = new_geoms; else { free(new_geoms); ret->geoms = NULL; ret->maxgeoms = 0; } return (LWGEOM*)ret; } #if POSTGIS_GEOS_VERSION < 38 /* * Fully node given linework */ static GEOSGeometry* LWGEOM_GEOS_nodeLines(const GEOSGeometry* lines) { /* GEOS3.7 GEOSNode fails on regression tests */ /* GEOS3.7 GEOSUnaryUnion fails on regression tests */ /* union of first point with geometry */ GEOSGeometry *unioned, *point; point = LWGEOM_GEOS_getPointN(lines, 0); if (!point) return NULL; unioned = GEOSUnion(lines, point); GEOSGeom_destroy(point); return unioned; } /* * We expect initGEOS being called already. * Will return NULL on error (expect error handler being called by then) */ static GEOSGeometry* LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry* gin) { GEOSGeom gout; GEOSGeom geos_bound; GEOSGeom geos_cut_edges, geos_area, collapse_points; GEOSGeometry* vgeoms[3]; /* One for area, one for cut-edges */ unsigned int nvgeoms = 0; assert(GEOSGeomTypeId(gin) == GEOS_POLYGON || GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON); geos_bound = GEOSBoundary(gin); if (!geos_bound) return NULL; /* Use noded boundaries as initial "cut" edges */ geos_cut_edges = LWGEOM_GEOS_nodeLines(geos_bound); if (!geos_cut_edges) { GEOSGeom_destroy(geos_bound); lwnotice("LWGEOM_GEOS_nodeLines(): %s", lwgeom_geos_errmsg); return NULL; } /* NOTE: the noding process may drop lines collapsing to points. * We want to retrieve any of those */ { GEOSGeometry* pi; GEOSGeometry* po; pi = GEOSGeom_extractUniquePoints(geos_bound); if (!pi) { GEOSGeom_destroy(geos_bound); lwnotice("GEOSGeom_extractUniquePoints(): %s", lwgeom_geos_errmsg); return NULL; } po = GEOSGeom_extractUniquePoints(geos_cut_edges); if (!po) { GEOSGeom_destroy(geos_bound); GEOSGeom_destroy(pi); lwnotice("GEOSGeom_extractUniquePoints(): %s", lwgeom_geos_errmsg); return NULL; } collapse_points = GEOSDifference(pi, po); if (!collapse_points) { GEOSGeom_destroy(geos_bound); GEOSGeom_destroy(pi); GEOSGeom_destroy(po); lwnotice("GEOSDifference(): %s", lwgeom_geos_errmsg); return NULL; } GEOSGeom_destroy(pi); GEOSGeom_destroy(po); } GEOSGeom_destroy(geos_bound); /* And use an empty geometry as initial "area" */ geos_area = GEOSGeom_createEmptyPolygon(); if (!geos_area) { lwnotice("GEOSGeom_createEmptyPolygon(): %s", lwgeom_geos_errmsg); GEOSGeom_destroy(geos_cut_edges); return NULL; } /* * See if an area can be build with the remaining edges * and if it can, symdifference with the original area. * Iterate this until no more polygons can be created * with left-over edges. */ while (GEOSGetNumGeometries(geos_cut_edges)) { GEOSGeometry* new_area = 0; GEOSGeometry* new_area_bound = 0; GEOSGeometry* symdif = 0; GEOSGeometry* new_cut_edges = 0; #ifdef LWGEOM_PROFILE_MAKEVALID lwnotice("ST_MakeValid: building area from %d edges", GEOSGetNumGeometries(geos_cut_edges)); #endif /* * ASSUMPTION: cut_edges should already be fully noded */ new_area = LWGEOM_GEOS_buildArea(geos_cut_edges); if (!new_area) /* must be an exception */ { GEOSGeom_destroy(geos_cut_edges); GEOSGeom_destroy(geos_area); lwnotice("LWGEOM_GEOS_buildArea() threw an error: %s", lwgeom_geos_errmsg); return NULL; } if (GEOSisEmpty(new_area)) { /* no more rings can be build with thes edges */ GEOSGeom_destroy(new_area); break; } /* * We succeeded in building a ring! * Save the new ring boundaries first (to compute * further cut edges later) */ new_area_bound = GEOSBoundary(new_area); if (!new_area_bound) { /* We did check for empty area already so this must be some other error */ lwnotice("GEOSBoundary('%s') threw an error: %s", lwgeom_to_ewkt(GEOS2LWGEOM(new_area, 0)), lwgeom_geos_errmsg); GEOSGeom_destroy(new_area); GEOSGeom_destroy(geos_area); return NULL; } /* * Now symdif new and old area */ symdif = GEOSSymDifference(geos_area, new_area); if (!symdif) /* must be an exception */ { GEOSGeom_destroy(geos_cut_edges); GEOSGeom_destroy(new_area); GEOSGeom_destroy(new_area_bound); GEOSGeom_destroy(geos_area); lwnotice("GEOSSymDifference() threw an error: %s", lwgeom_geos_errmsg); return NULL; } GEOSGeom_destroy(geos_area); GEOSGeom_destroy(new_area); geos_area = symdif; symdif = 0; /* * Now let's re-set geos_cut_edges with what's left * from the original boundary. * ASSUMPTION: only the previous cut-edges can be * left, so we don't need to reconsider * the whole original boundaries * * NOTE: this is an expensive operation. * */ new_cut_edges = GEOSDifference(geos_cut_edges, new_area_bound); GEOSGeom_destroy(new_area_bound); if (!new_cut_edges) /* an exception ? */ { /* cleanup and throw */ GEOSGeom_destroy(geos_cut_edges); GEOSGeom_destroy(geos_area); lwerror("GEOSDifference() threw an error: %s", lwgeom_geos_errmsg); return NULL; } GEOSGeom_destroy(geos_cut_edges); geos_cut_edges = new_cut_edges; } if (!GEOSisEmpty(geos_area)) vgeoms[nvgeoms++] = geos_area; else GEOSGeom_destroy(geos_area); if (!GEOSisEmpty(geos_cut_edges)) vgeoms[nvgeoms++] = geos_cut_edges; else GEOSGeom_destroy(geos_cut_edges); if (!GEOSisEmpty(collapse_points)) vgeoms[nvgeoms++] = collapse_points; else GEOSGeom_destroy(collapse_points); if (1 == nvgeoms) { /* Return cut edges */ gout = vgeoms[0]; } else { /* Collect areas and lines (if any line) */ gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms); if (!gout) /* an exception again */ { /* cleanup and throw */ lwerror("GEOSGeom_createCollection() threw an error: %s", lwgeom_geos_errmsg); /* TODO: cleanup! */ return NULL; } } return gout; } static GEOSGeometry* LWGEOM_GEOS_makeValidLine(const GEOSGeometry* gin) { GEOSGeometry* noded; noded = LWGEOM_GEOS_nodeLines(gin); return noded; } static GEOSGeometry* LWGEOM_GEOS_makeValidMultiLine(const GEOSGeometry* gin) { GEOSGeometry** lines; GEOSGeometry** points; GEOSGeometry* mline_out = 0; GEOSGeometry* mpoint_out = 0; GEOSGeometry* gout = 0; uint32_t nlines = 0, nlines_alloc; uint32_t npoints = 0; uint32_t ngeoms = 0, nsubgeoms; uint32_t i, j; ngeoms = GEOSGetNumGeometries(gin); nlines_alloc = ngeoms; lines = lwalloc(sizeof(GEOSGeometry*) * nlines_alloc); points = lwalloc(sizeof(GEOSGeometry*) * ngeoms); for (i = 0; i < ngeoms; ++i) { const GEOSGeometry* g = GEOSGetGeometryN(gin, i); GEOSGeometry* vg; vg = LWGEOM_GEOS_makeValidLine(g); /* Drop any invalid or empty geometry */ if (!vg) continue; if (GEOSisEmpty(vg)) { GEOSGeom_destroy(vg); continue; } if (GEOSGeomTypeId(vg) == GEOS_POINT) points[npoints++] = vg; else if (GEOSGeomTypeId(vg) == GEOS_LINESTRING) lines[nlines++] = vg; else if (GEOSGeomTypeId(vg) == GEOS_MULTILINESTRING) { nsubgeoms = GEOSGetNumGeometries(vg); nlines_alloc += nsubgeoms; lines = lwrealloc(lines, sizeof(GEOSGeometry*) * nlines_alloc); for (j = 0; j < nsubgeoms; ++j) { const GEOSGeometry* gc = GEOSGetGeometryN(vg, j); /* NOTE: ownership of the cloned geoms will be * taken by final collection */ lines[nlines++] = GEOSGeom_clone(gc); } } else { /* NOTE: return from GEOSGeomType will leak * but we really don't expect this to happen */ lwerror("unexpected geom type returned by LWGEOM_GEOS_makeValid: %s", GEOSGeomType(vg)); } } if (npoints) { if (npoints > 1) mpoint_out = GEOSGeom_createCollection(GEOS_MULTIPOINT, points, npoints); else mpoint_out = points[0]; } if (nlines) { if (nlines > 1) mline_out = GEOSGeom_createCollection(GEOS_MULTILINESTRING, lines, nlines); else mline_out = lines[0]; } lwfree(lines); if (mline_out && mpoint_out) { points[0] = mline_out; points[1] = mpoint_out; gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, points, 2); } else if (mline_out) gout = mline_out; else if (mpoint_out) gout = mpoint_out; lwfree(points); return gout; } /* * We expect initGEOS being called already. * Will return NULL on error (expect error handler being called by then) */ static GEOSGeometry* LWGEOM_GEOS_makeValidCollection(const GEOSGeometry* gin) { int nvgeoms; GEOSGeometry** vgeoms; GEOSGeom gout; int i; nvgeoms = GEOSGetNumGeometries(gin); if (nvgeoms == -1) { lwerror("GEOSGetNumGeometries: %s", lwgeom_geos_errmsg); return 0; } vgeoms = lwalloc(sizeof(GEOSGeometry*) * nvgeoms); if (!vgeoms) { lwerror("LWGEOM_GEOS_makeValidCollection: out of memory"); return 0; } for (i = 0; i < nvgeoms; ++i) { vgeoms[i] = LWGEOM_GEOS_makeValid(GEOSGetGeometryN(gin, i)); if (!vgeoms[i]) { int j; for (j = 0; j < i - 1; j++) GEOSGeom_destroy(vgeoms[j]); lwfree(vgeoms); /* we expect lwerror being called already by makeValid */ return NULL; } } /* Collect areas and lines (if any line) */ gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms); if (!gout) /* an exception again */ { /* cleanup and throw */ for (i = 0; i < nvgeoms; ++i) GEOSGeom_destroy(vgeoms[i]); lwfree(vgeoms); lwerror("GEOSGeom_createCollection() threw an error: %s", lwgeom_geos_errmsg); return NULL; } lwfree(vgeoms); return gout; } GEOSGeometry* LWGEOM_GEOS_makeValid(const GEOSGeometry* gin) { GEOSGeometry* gout; char ret_char; #if POSTGIS_DEBUG_LEVEL >= 3 LWGEOM *geos_geom; char *geom_ewkt; #endif /* * Step 2: return what we got so far if already valid */ ret_char = GEOSisValid(gin); if (ret_char == 2) { /* I don't think should ever happen */ lwerror("GEOSisValid(): %s", lwgeom_geos_errmsg); return NULL; } else if (ret_char) { #if POSTGIS_DEBUG_LEVEL >= 3 geos_geom = GEOS2LWGEOM(gin, 0); geom_ewkt = lwgeom_to_ewkt(geos_geom); LWDEBUGF(3, "Geometry [%s] is valid. ", geom_ewkt); lwgeom_free(geos_geom); lwfree(geom_ewkt); #endif /* It's valid at this step, return what we have */ return GEOSGeom_clone(gin); } #if POSTGIS_DEBUG_LEVEL >= 3 geos_geom = GEOS2LWGEOM(gin, 0); geom_ewkt = lwgeom_to_ewkt(geos_geom); LWDEBUGF(3, "Geometry [%s] is still not valid: %s. Will try to clean up further.", geom_ewkt, lwgeom_geos_errmsg); lwgeom_free(geos_geom); lwfree(geom_ewkt); #endif /* * Step 3 : make what we got valid */ switch (GEOSGeomTypeId(gin)) { case GEOS_MULTIPOINT: case GEOS_POINT: /* points are always valid, but we might have invalid ordinate values */ lwnotice("PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up"); return NULL; break; case GEOS_LINESTRING: gout = LWGEOM_GEOS_makeValidLine(gin); if (!gout) /* an exception or something */ { /* cleanup and throw */ lwerror("%s", lwgeom_geos_errmsg); return NULL; } break; /* we've done */ case GEOS_MULTILINESTRING: gout = LWGEOM_GEOS_makeValidMultiLine(gin); if (!gout) /* an exception or something */ { /* cleanup and throw */ lwerror("%s", lwgeom_geos_errmsg); return NULL; } break; /* we've done */ case GEOS_POLYGON: case GEOS_MULTIPOLYGON: { gout = LWGEOM_GEOS_makeValidPolygon(gin); if (!gout) /* an exception or something */ { /* cleanup and throw */ lwerror("%s", lwgeom_geos_errmsg); return NULL; } break; /* we've done */ } case GEOS_GEOMETRYCOLLECTION: { gout = LWGEOM_GEOS_makeValidCollection(gin); if (!gout) /* an exception or something */ { /* cleanup and throw */ lwerror("%s", lwgeom_geos_errmsg); return NULL; } break; /* we've done */ } default: { char* typname = GEOSGeomType(gin); lwnotice("ST_MakeValid: doesn't support geometry type: %s", typname); GEOSFree(typname); return NULL; break; } } #if PARANOIA_LEVEL > 1 /* * Now check if every point of input is also found in output, or abort by returning NULL * * Input geometry was lwgeom_in */ { int loss; GEOSGeometry *pi, *po, *pd; /* TODO: handle some errors here... * Lack of exceptions is annoying indeed, * I'm getting old --strk; */ pi = GEOSGeom_extractUniquePoints(gin); po = GEOSGeom_extractUniquePoints(gout); pd = GEOSDifference(pi, po); /* input points - output points */ GEOSGeom_destroy(pi); GEOSGeom_destroy(po); loss = pd && !GEOSisEmpty(pd); GEOSGeom_destroy(pd); if (loss) { lwnotice("%s [%d] Vertices lost in LWGEOM_GEOS_makeValid", __FILE__, __LINE__); /* return NULL */ } } #endif /* PARANOIA_LEVEL > 1 */ return gout; } #endif /* Exported. Uses GEOS internally */ LWGEOM* lwgeom_make_valid(LWGEOM* lwgeom_in) { int is3d; GEOSGeom geosgeom; GEOSGeometry* geosout; LWGEOM* lwgeom_out; is3d = FLAGS_GET_Z(lwgeom_in->flags); /* * Step 1 : try to convert to GEOS, if impossible, clean that up first * otherwise (adding only duplicates of existing points) */ initGEOS(lwgeom_geos_error, lwgeom_geos_error); lwgeom_out = lwgeom_in; geosgeom = LWGEOM2GEOS(lwgeom_out, 1); if (!geosgeom) { LWDEBUGF(4, "Original geom can't be converted to GEOS (%s)" " - will try cleaning that up first", lwgeom_geos_errmsg); lwgeom_out = lwgeom_make_geos_friendly(lwgeom_out); if (!lwgeom_out) lwerror("Could not make a valid geometry out of input"); /* try again as we did cleanup now */ /* TODO: invoke LWGEOM2GEOS directly with autoclean ? */ geosgeom = LWGEOM2GEOS(lwgeom_out, 0); if (!geosgeom) { lwerror("Couldn't convert POSTGIS geom to GEOS: %s", lwgeom_geos_errmsg); return NULL; } } else { LWDEBUG(4, "original geom converted to GEOS"); lwgeom_out = lwgeom_in; } #if POSTGIS_GEOS_VERSION < 38 geosout = LWGEOM_GEOS_makeValid(geosgeom); #else geosout = GEOSMakeValid(geosgeom); #endif GEOSGeom_destroy(geosgeom); if (!geosout) return NULL; lwgeom_out = GEOS2LWGEOM(geosout, is3d); GEOSGeom_destroy(geosout); if (lwgeom_is_collection(lwgeom_in) && !lwgeom_is_collection(lwgeom_out)) { LWGEOM** ogeoms = lwalloc(sizeof(LWGEOM*)); LWGEOM* ogeom; LWDEBUG(3, "lwgeom_make_valid: forcing multi"); /* NOTE: this is safe because lwgeom_out is surely not lwgeom_in or * otherwise we couldn't have a collection and a non-collection */ assert(lwgeom_in != lwgeom_out); ogeoms[0] = lwgeom_out; ogeom = (LWGEOM*)lwcollection_construct( MULTITYPE[lwgeom_out->type], lwgeom_out->srid, lwgeom_out->bbox, 1, ogeoms); lwgeom_out->bbox = NULL; lwgeom_out = ogeom; } lwgeom_out->srid = lwgeom_in->srid; return lwgeom_out; } lwgeom/src/liblwgeom/lwiterator.c0000644000176200001440000001354613773172540016676 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * **********************************************************************/ #include "liblwgeom.h" #include "lwgeom_log.h" struct LISTNODE { struct LISTNODE* next; void* item; }; typedef struct LISTNODE LISTNODE; /* The LWPOINTITERATOR consists of two stacks of items to process: a stack * of geometries, and a stack of POINTARRAYs extracted from those geometries. * The index "i" refers to the "next" point, which is found at the top of the * pointarrays stack. * * When the pointarrays stack is depleted, we pull a geometry from the geometry * stack to replenish it. */ struct LWPOINTITERATOR { LISTNODE* geoms; LISTNODE* pointarrays; uint32_t i; char allow_modification; }; static LISTNODE* prepend_node(void* g, LISTNODE* front) { LISTNODE* n = lwalloc(sizeof(LISTNODE)); n->item = g; n->next = front; return n; } static LISTNODE* pop_node(LISTNODE* i) { LISTNODE* next = i->next; lwfree(i); return next; } static int add_lwgeom_to_stack(LWPOINTITERATOR* s, LWGEOM* g) { if (lwgeom_is_empty(g)) return LW_FAILURE; s->geoms = prepend_node(g, s->geoms); return LW_SUCCESS; } /** Return a pointer to the first of one or more LISTNODEs holding the POINTARRAYs * of a geometry. Will not handle GeometryCollections. */ static LISTNODE* extract_pointarrays_from_lwgeom(LWGEOM* g) { switch(lwgeom_get_type(g)) { case POINTTYPE: return prepend_node(lwgeom_as_lwpoint(g)->point, NULL); case LINETYPE: return prepend_node(lwgeom_as_lwline(g)->points, NULL); case TRIANGLETYPE: return prepend_node(lwgeom_as_lwtriangle(g)->points, NULL); case CIRCSTRINGTYPE: return prepend_node(lwgeom_as_lwcircstring(g)->points, NULL); case POLYGONTYPE: { LISTNODE* n = NULL; LWPOLY* p = lwgeom_as_lwpoly(g); int i; for (i = p->nrings - 1; i >= 0; i--) n = prepend_node(p->rings[i], n); return n; } default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type)); } return NULL; } /** Remove an LWCOLLECTION from the iterator stack, and add the components of the * LWCOLLECTIONs to the stack. */ static void unroll_collection(LWPOINTITERATOR* s) { int i; LWCOLLECTION* c; if (!s->geoms) { return; } c = (LWCOLLECTION*) s->geoms->item; s->geoms = pop_node(s->geoms); for (i = c->ngeoms - 1; i >= 0; i--) { LWGEOM* g = lwcollection_getsubgeom(c, i); add_lwgeom_to_stack(s, g); } } /** Unroll LWCOLLECTIONs from the top of the stack, as necessary, until the element at the * top of the stack is not a LWCOLLECTION. */ static void unroll_collections(LWPOINTITERATOR* s) { while(s->geoms && lwgeom_is_collection(s->geoms->item)) { unroll_collection(s); } } static int lwpointiterator_advance(LWPOINTITERATOR* s) { s->i += 1; /* We've reached the end of our current POINTARRAY. Try to see if there * are any more POINTARRAYS on the stack. */ if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints) { s->pointarrays = pop_node(s->pointarrays); s->i = 0; } /* We don't have a current POINTARRAY. Pull a geometry from the stack, and * decompose it into its POINTARRARYs. */ if (!s->pointarrays) { LWGEOM* g; unroll_collections(s); if (!s->geoms) { return LW_FAILURE; } s->i = 0; g = s->geoms->item; s->pointarrays = extract_pointarrays_from_lwgeom(g); s->geoms = pop_node(s->geoms); } if (!s->pointarrays) { return LW_FAILURE; } return LW_SUCCESS; } /* Public API implementation */ int lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p) { if (!lwpointiterator_has_next(s)) return LW_FAILURE; return getPoint4d_p(s->pointarrays->item, s->i, p); } int lwpointiterator_has_next(LWPOINTITERATOR* s) { if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints) return LW_TRUE; return LW_FALSE; } int lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p) { if (!lwpointiterator_has_next(s)) return LW_FAILURE; /* If p is NULL, just advance without reading */ if (p && !lwpointiterator_peek(s, p)) return LW_FAILURE; lwpointiterator_advance(s); return LW_SUCCESS; } int lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p) { if (!lwpointiterator_has_next(s)) return LW_FAILURE; if (!s->allow_modification) { lwerror("Cannot write to read-only iterator"); return LW_FAILURE; } ptarray_set_point4d(s->pointarrays->item, s->i, p); lwpointiterator_advance(s); return LW_SUCCESS; } LWPOINTITERATOR* lwpointiterator_create(const LWGEOM* g) { LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g); it->allow_modification = LW_FALSE; return it; } LWPOINTITERATOR* lwpointiterator_create_rw(LWGEOM* g) { LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR)); it->geoms = NULL; it->pointarrays = NULL; it->i = 0; it->allow_modification = LW_TRUE; add_lwgeom_to_stack(it, g); lwpointiterator_advance(it); return it; } void lwpointiterator_destroy(LWPOINTITERATOR* s) { while (s->geoms != NULL) { s->geoms = pop_node(s->geoms); } while (s->pointarrays != NULL) { s->pointarrays = pop_node(s->pointarrays); } lwfree(s); } lwgeom/src/liblwgeom/DIFFS0000644000176200001440000000042713773172540015106 0ustar liggesusersTue Jan 28 09:42:00 CET 2020 source from https://download.osgeo.org/postgis/source/postgis-3.0.0.tar.gz removed files lwgeom_sfcgal.* lwutil.c: remove fprintf(), exit(), stderr lwin_wkt_lex: remove exit(); remove stderr, stdout lwgeodetic_tree.c: circ_tree_print: outcommented lwgeom/src/liblwgeom/lwin_encoded_polyline.c0000644000176200001440000000426013773172540021040 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2014 Kashif Rasul and * **********************************************************************/ #include #include #include #include "liblwgeom.h" #include "../postgis_config.h" LWGEOM* lwgeom_from_encoded_polyline(const char *encodedpolyline, int precision) { LWGEOM *geom = NULL; POINTARRAY *pa = NULL; int length = strlen(encodedpolyline); int idx = 0; double scale = pow(10,precision); float latitude = 0.0f; float longitude = 0.0f; pa = ptarray_construct_empty(LW_FALSE, LW_FALSE, 1); while (idx < length) { POINT4D pt; char byte = 0; int res = 0; char shift = 0; do { byte = encodedpolyline[idx++] - 63; res |= (byte & 0x1F) << shift; shift += 5; } while (byte >= 0x20); float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1)); latitude += deltaLat; shift = 0; res = 0; do { byte = encodedpolyline[idx++] - 63; res |= (byte & 0x1F) << shift; shift += 5; } while (byte >= 0x20); float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1)); longitude += deltaLon; pt.x = longitude/scale; pt.y = latitude/scale; pt.m = pt.z = 0.0; ptarray_append_point(pa, &pt, LW_FALSE); } geom = (LWGEOM *)lwline_construct(4326, NULL, pa); lwgeom_add_bbox(geom); return geom; } lwgeom/src/liblwgeom/bytebuffer.h0000644000176200001440000000530713773172540016640 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Nicklas Avén * **********************************************************************/ #ifndef _BYTEBUFFER_H #define _BYTEBUFFER_H 1 #include #include #include #include "varint.h" #include "lwgeom_log.h" #define BYTEBUFFER_STARTSIZE 512 #define BYTEBUFFER_STATICSIZE 1024 typedef struct { size_t capacity; uint8_t *buf_start; uint8_t *writecursor; uint8_t *readcursor; uint8_t buf_static[BYTEBUFFER_STATICSIZE]; } bytebuffer_t; void bytebuffer_init_with_size(bytebuffer_t *b, size_t size); void bytebuffer_destroy_buffer(bytebuffer_t *s); void bytebuffer_append_byte(bytebuffer_t *s, const uint8_t val); void bytebuffer_append_bytebuffer(bytebuffer_t *write_to, bytebuffer_t *write_from); void bytebuffer_append_varint(bytebuffer_t *s, const int64_t val); void bytebuffer_append_uvarint(bytebuffer_t *s, const uint64_t val); size_t bytebuffer_getlength(const bytebuffer_t *s); uint8_t* bytebuffer_get_buffer_copy(const bytebuffer_t *s, size_t *buffer_length); const uint8_t* bytebuffer_get_buffer(const bytebuffer_t *s, size_t *buffer_length); /* Unused functions */ #if 0 void bytebuffer_destroy(bytebuffer_t *s); bytebuffer_t *bytebuffer_create_with_size(size_t size); bytebuffer_t *bytebuffer_create(void); void bytebuffer_clear(bytebuffer_t *s); uint64_t bytebuffer_read_uvarint(bytebuffer_t *s); int64_t bytebuffer_read_varint(bytebuffer_t *s); bytebuffer_t* bytebuffer_merge(bytebuffer_t **buff_array, int nbuffers); void bytebuffer_reset_reading(bytebuffer_t *s); void bytebuffer_append_bytebuffer(bytebuffer_t *write_to,bytebuffer_t *write_from); void bytebuffer_append_bulk(bytebuffer_t *s, void * start, size_t size); void bytebuffer_append_int(bytebuffer_t *buf, const int val, int swap); void bytebuffer_append_double(bytebuffer_t *buf, const double val, int swap); #endif #endif /* _BYTEBUFFER_H */ lwgeom/src/liblwgeom/lwunionfind.c0000644000176200001440000001073313773172540017031 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * **********************************************************************/ #include "liblwgeom.h" #include "lwunionfind.h" #include #include static int cmp_int(const void *a, const void *b); static int cmp_int_ptr(const void *a, const void *b); UNIONFIND* UF_create(uint32_t N) { size_t i; UNIONFIND* uf = lwalloc(sizeof(UNIONFIND)); uf->N = N; uf->num_clusters = N; uf->clusters = lwalloc(N * sizeof(uint32_t)); uf->cluster_sizes = lwalloc(N * sizeof(uint32_t)); for (i = 0; i < N; i++) { uf->clusters[i] = i; uf->cluster_sizes[i] = 1; } return uf; } void UF_destroy(UNIONFIND* uf) { lwfree(uf->clusters); lwfree(uf->cluster_sizes); lwfree(uf); } uint32_t UF_find (UNIONFIND* uf, uint32_t i) { uint32_t base = i; while (uf->clusters[base] != base) { base = uf->clusters[base]; } while (i != base) { uint32_t next = uf->clusters[i]; uf->clusters[i] = base; i = next; } return i; } uint32_t UF_size (UNIONFIND* uf, uint32_t i) { return uf->cluster_sizes[UF_find(uf, i)]; } void UF_union(UNIONFIND* uf, uint32_t i, uint32_t j) { uint32_t a = UF_find(uf, i); uint32_t b = UF_find(uf, j); if (a == b) { return; } if (uf->cluster_sizes[a] < uf->cluster_sizes[b] || (uf->cluster_sizes[a] == uf->cluster_sizes[b] && a > b)) { uf->clusters[a] = uf->clusters[b]; uf->cluster_sizes[b] += uf->cluster_sizes[a]; uf->cluster_sizes[a] = 0; } else { uf->clusters[b] = uf->clusters[a]; uf->cluster_sizes[a] += uf->cluster_sizes[b]; uf->cluster_sizes[b] = 0; } uf->num_clusters--; } uint32_t* UF_ordered_by_cluster(UNIONFIND* uf) { size_t i; uint32_t** cluster_id_ptr_by_elem_id = lwalloc(uf->N * sizeof (uint32_t*)); uint32_t* ordered_ids = lwalloc(uf->N * sizeof (uint32_t)); for (i = 0; i < uf->N; i++) { /* Make sure each value in uf->clusters is pointing to the * root of the cluster. * */ UF_find(uf, i); cluster_id_ptr_by_elem_id[i] = &(uf->clusters[i]); } /* Sort the array of cluster id pointers, so that pointers to the * same cluster id are grouped together. * */ qsort(cluster_id_ptr_by_elem_id, uf->N, sizeof (uint32_t*), &cmp_int_ptr); /* Recover the input element ids from the cluster id pointers, so * we can return element ids grouped by cluster id. * */ for (i = 0; i < uf-> N; i++) { ordered_ids[i] = (cluster_id_ptr_by_elem_id[i] - uf->clusters); } lwfree(cluster_id_ptr_by_elem_id); return ordered_ids; } uint32_t* UF_get_collapsed_cluster_ids(UNIONFIND* uf, const char* is_in_cluster) { uint32_t* ordered_components = UF_ordered_by_cluster(uf); uint32_t* new_ids = lwalloc(uf->N * sizeof(uint32_t)); uint32_t last_old_id, current_new_id, i; char encountered_cluster = LW_FALSE; current_new_id = 0; last_old_id = 0; for (i = 0; i < uf->N; i++) { uint32_t j = ordered_components[i]; if (!is_in_cluster || is_in_cluster[j]) { uint32_t current_old_id = UF_find(uf, j); if (!encountered_cluster) { encountered_cluster = LW_TRUE; last_old_id = current_old_id; } if (current_old_id != last_old_id) current_new_id++; new_ids[j] = current_new_id; last_old_id = current_old_id; } } lwfree(ordered_components); return new_ids; } static int cmp_int(const void *a, const void *b) { if (*((uint32_t*) a) > *((uint32_t*) b)) { return 1; } else if (*((uint32_t*) a) < *((uint32_t*) b)) { return -1; } else { return 0; } } static int cmp_int_ptr(const void *a, const void *b) { int val_cmp = cmp_int(*((uint32_t**) a), *((uint32_t**) b)); if (val_cmp != 0) { return val_cmp; } if (a > b) { return 1; } if (a < b) { return -1; } return 0; } lwgeom/src/liblwgeom/lwout_gml.c0000644000176200001440000016316214343212734016505 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2010-2012 Oslandia * Copyright 2001-2003 Refractions Research Inc. * **********************************************************************/ /** * @file GML output routines. * **********************************************************************/ #include #include "liblwgeom_internal.h" static size_t asgml2_point_size(const LWPOINT *point, const char *srs, int precision, const char *prefix); static char *asgml2_point(const LWPOINT *point, const char *srs, int precision, const char *prefix); static size_t asgml2_line_size(const LWLINE *line, const char *srs, int precision, const char *prefix); static char *asgml2_line(const LWLINE *line, const char *srs, int precision, const char *prefix); static size_t asgml2_poly_size(const LWPOLY *poly, const char *srs, int precision, const char *prefix); static char *asgml2_poly(const LWPOLY *poly, const char *srs, int precision, const char *prefix); static size_t asgml2_multi_size(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix); static char *asgml2_multi(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix); static size_t asgml2_collection_size(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix); static char *asgml2_collection(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix); static size_t pointArray_toGML2(POINTARRAY *pa, char *buf, int precision); static size_t asgml3_point_size(const LWPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_point(const LWPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_line_size(const LWLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_line(const LWLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_circstring( const LWCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id ); static size_t asgml3_poly_size(const LWPOLY *poly, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_poly(const LWPOLY *poly, const char *srs, int precision, int opts, int is_patch, const char *prefix, const char *id); static char * asgml3_curvepoly(const LWCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_triangle_size(const LWTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_triangle(const LWTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_multi_size(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_multi(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_psurface(const LWPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_tin(const LWTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_collection_size(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_collection(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char *asgml3_compound(const LWCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ); static char *asgml3_multicurve( const LWMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ); static char *asgml3_multisurface(const LWMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t pointArray_toGML3(POINTARRAY *pa, char *buf, int precision, int opts); static size_t pointArray_GMLsize(POINTARRAY *pa, int precision); static char * gbox_to_gml2(const GBOX *bbox, const char *srs, int precision, const char *prefix) { int size; POINT4D pt; POINTARRAY *pa; char *ptr, *output; size_t prefixlen = strlen(prefix); if ( ! bbox ) { size = ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = lwalloc(size); ptr += snprintf(ptr, strlen(ptr), "<%sBox", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); ptr += snprintf(ptr, strlen(ptr), "/>"); return output; } pa = ptarray_construct_empty(FLAGS_GET_Z(bbox->flags), 0, 2); pt.x = bbox->xmin; pt.y = bbox->ymin; if (FLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmin; ptarray_append_point(pa, &pt, LW_TRUE); pt.x = bbox->xmax; pt.y = bbox->ymax; if (FLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmax; ptarray_append_point(pa, &pt, LW_TRUE); size = pointArray_GMLsize(pa, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = lwalloc(size); if ( srs ) ptr += snprintf(ptr, strlen(ptr), "<%sBox srsName=\"%s\">", prefix, srs); else ptr += snprintf(ptr, strlen(ptr), "<%sBox>", prefix); ptr += snprintf(ptr, strlen(ptr), "<%scoordinates>", prefix); ptr += pointArray_toGML2(pa, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); ptarray_free(pa); return output; } static char * gbox_to_gml3(const GBOX *bbox, const char *srs, int precision, int opts, const char *prefix) { int size; POINT4D pt; POINTARRAY *pa; char *ptr, *output; size_t prefixlen = strlen(prefix); int dimension = 2; if ( ! bbox ) { size = ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = lwalloc(size); ptr += snprintf(ptr, strlen(ptr), "<%sEnvelope", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); ptr += snprintf(ptr, strlen(ptr), "/>"); return output; } if (FLAGS_GET_Z(bbox->flags)) dimension = 3; pa = ptarray_construct_empty(FLAGS_GET_Z(bbox->flags), 0, 1); pt.x = bbox->xmin; pt.y = bbox->ymin; if (FLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmin; ptarray_append_point(pa, &pt, LW_TRUE); size = pointArray_GMLsize(pa, precision) * 2; size += ( sizeof("//") + (prefixlen*3) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); if ( IS_DIMS(opts) ) size += sizeof(" srsDimension=. ."); ptr = output = lwalloc(size); ptr += snprintf(ptr, strlen(ptr), "<%sEnvelope", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if ( IS_DIMS(opts) ) ptr += snprintf(ptr, strlen(ptr), " srsDimension=\"%d\"", dimension); ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%slowerCorner>", prefix); ptr += pointArray_toGML3(pa, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptarray_remove_point(pa, 0); pt.x = bbox->xmax; pt.y = bbox->ymax; if (FLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmax; ptarray_append_point(pa, &pt, LW_TRUE); ptr += snprintf(ptr, strlen(ptr), "<%supperCorner>", prefix); ptr += pointArray_toGML3(pa, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptarray_free(pa); return output; } extern char * lwgeom_extent_to_gml2(const LWGEOM *geom, const char *srs, int precision, const char *prefix) { const GBOX* bbox = lwgeom_get_bbox(geom); /* if ( ! bbox ) { lwerror("lwgeom_extent_to_gml2: empty geometry doesn't have a bounding box"); return NULL; } */ char *ret = gbox_to_gml2(bbox, srs, precision, prefix); return ret; } extern char * lwgeom_extent_to_gml3(const LWGEOM *geom, const char *srs, int precision, int opts, const char *prefix) { const GBOX* bbox = lwgeom_get_bbox(geom); /* if ( ! bbox ) { lwerror("lwgeom_extent_to_gml3: empty geometry doesn't have a bounding box"); return NULL; } */ return gbox_to_gml3(bbox, srs, precision, opts, prefix); } /** * @brief VERSION GML 2 * takes a GEOMETRY and returns a GML2 representation */ extern char * lwgeom_to_gml2(const LWGEOM *geom, const char *srs, int precision, const char* prefix) { int type = geom->type; /* Return null for empty (#1377) */ if ( lwgeom_is_empty(geom) ) return NULL; switch (type) { case POINTTYPE: return asgml2_point((LWPOINT*)geom, srs, precision, prefix); case LINETYPE: return asgml2_line((LWLINE*)geom, srs, precision, prefix); case POLYGONTYPE: return asgml2_poly((LWPOLY*)geom, srs, precision, prefix); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: return asgml2_multi((LWCOLLECTION*)geom, srs, precision, prefix); case COLLECTIONTYPE: return asgml2_collection((LWCOLLECTION*)geom, srs, precision, prefix); case TRIANGLETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: lwerror("Cannot convert %s to GML2. Try ST_AsGML(3, ) to generate GML3.", lwtype_name(type)); return NULL; default: lwerror("lwgeom_to_gml2: '%s' geometry type not supported", lwtype_name(type)); return NULL; } } static size_t asgml2_point_size(const LWPOINT *point, const char *srs, int precision, const char* prefix) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(point->point, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); return size; } static size_t asgml2_point_buf(const LWPOINT *point, const char *srs, char *output, int precision, const char* prefix) { char *ptr = output; ptr += snprintf(ptr, strlen(ptr), "<%sPoint", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if ( lwpoint_is_empty(point) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%scoordinates>", prefix); ptr += pointArray_toGML2(point->point, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); return (ptr-output); } static char * asgml2_point(const LWPOINT *point, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_point_size(point, srs, precision, prefix); output = lwalloc(size); asgml2_point_buf(point, srs, output, precision, prefix); return output; } static size_t asgml2_line_size(const LWLINE *line, const char *srs, int precision, const char *prefix) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(line->points, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); return size; } static size_t asgml2_line_buf(const LWLINE *line, const char *srs, char *output, int precision, const char *prefix) { char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "<%sLineString", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if ( lwline_is_empty(line) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%scoordinates>", prefix); ptr += pointArray_toGML2(line->points, ptr, precision); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); return (ptr-output); } static char * asgml2_line(const LWLINE *line, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_line_size(line, srs, precision, prefix); output = lwalloc(size); asgml2_line_buf(line, srs, output, precision, prefix); return output; } static size_t asgml2_poly_size(const LWPOLY *poly, const char *srs, int precision, const char *prefix) { size_t size; uint32_t i; size_t prefixlen = strlen(prefix); size = sizeof("") + prefixlen*2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); if ( lwpoly_is_empty(poly) ) return size; size += ( sizeof("/") + ( prefixlen*3) ) * 2; size += ( sizeof("/") + ( prefixlen*2) ) * 2 * poly->nrings; for (i=0; inrings; i++) size += pointArray_GMLsize(poly->rings[i], precision); return size; } static size_t asgml2_poly_buf(const LWPOLY *poly, const char *srs, char *output, int precision, const char *prefix) { uint32_t i; char *ptr=output; ptr += snprintf(ptr, strlen(ptr), "<%sPolygon", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if ( lwpoly_is_empty(poly) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%souterBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); ptr += pointArray_toGML2(poly->rings[0], ptr, precision); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix, prefix); for (i=1; inrings; i++) { ptr += snprintf(ptr, strlen(ptr), "<%sinnerBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); ptr += pointArray_toGML2(poly->rings[i], ptr, precision); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix, prefix); } ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } static char * asgml2_poly(const LWPOLY *poly, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_poly_size(poly, srs, precision, prefix); output = lwalloc(size); asgml2_poly_buf(poly, srs, output, precision, prefix); return output; } /* * Compute max size required for GML version of this * inspected geometry. Will recurse when needed. * Don't call this with single-geoms inspected. */ static size_t asgml2_multi_size(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); LWGEOM *subgeom; /* the longest possible multi version */ size = sizeof(""); size += 2*prefixlen; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == POINTTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_point_size((LWPOINT*)subgeom, 0, precision, prefix); } else if (subgeom->type == LINETYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_line_size((LWLINE*)subgeom, 0, precision, prefix); } else if (subgeom->type == POLYGONTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_poly_size((LWPOLY*)subgeom, 0, precision, prefix); } } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml2_multi_buf(const LWCOLLECTION *col, const char *srs, char *output, int precision, const char *prefix) { int type = col->type; char *ptr, *gmltype; uint32_t i; LWGEOM *subgeom; ptr = output; gmltype=""; if (type == MULTIPOINTTYPE) gmltype = "MultiPoint"; else if (type == MULTILINETYPE) gmltype = "MultiLineString"; else if (type == MULTIPOLYGONTYPE) gmltype = "MultiPolygon"; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%s%s", prefix, gmltype); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (!col->ngeoms) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == POINTTYPE) { ptr += snprintf(ptr, strlen(ptr), "<%spointMember>", prefix); ptr += asgml2_point_buf((LWPOINT*)subgeom, 0, ptr, precision, prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); } else if (subgeom->type == LINETYPE) { ptr += snprintf(ptr, strlen(ptr), "<%slineStringMember>", prefix); ptr += asgml2_line_buf((LWLINE*)subgeom, 0, ptr, precision, prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); } else if (subgeom->type == POLYGONTYPE) { ptr += snprintf(ptr, strlen(ptr), "<%spolygonMember>", prefix); ptr += asgml2_poly_buf((LWPOLY*)subgeom, 0, ptr, precision, prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); } } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix, gmltype); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml2_multi(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix) { char *gml; size_t size; size = asgml2_multi_size(col, srs, precision, prefix); gml = lwalloc(size); asgml2_multi_buf(col, srs, gml, precision, prefix); return gml; } /* * Don't call this with single-geoms! */ static size_t asgml2_collection_size(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); LWGEOM *subgeom; size = sizeof(""); size += (prefixlen * 2); if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += ( sizeof("/") + prefixlen ) * 2; if ( subgeom->type == POINTTYPE) { size += asgml2_point_size((LWPOINT*)subgeom, 0, precision, prefix); } else if ( subgeom->type == LINETYPE) { size += asgml2_line_size((LWLINE*)subgeom, 0, precision, prefix); } else if ( subgeom->type == POLYGONTYPE) { size += asgml2_poly_size((LWPOLY*)subgeom, 0, precision, prefix); } else if ( lwgeom_is_collection(subgeom) ) { size += asgml2_collection_size((LWCOLLECTION*)subgeom, 0, precision, prefix); } else lwerror("asgml2_collection_size: Unable to process geometry type!"); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml2_collection_buf(const LWCOLLECTION *col, const char *srs, char *output, int precision, const char *prefix) { char *ptr; uint32_t i; LWGEOM *subgeom; ptr = output; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%sMultiGeometry", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (!col->ngeoms) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; ptr += snprintf(ptr, strlen(ptr), "<%sgeometryMember>", prefix); if (subgeom->type == POINTTYPE) { ptr += asgml2_point_buf((LWPOINT*)subgeom, 0, ptr, precision, prefix); } else if (subgeom->type == LINETYPE) { ptr += asgml2_line_buf((LWLINE*)subgeom, 0, ptr, precision, prefix); } else if (subgeom->type == POLYGONTYPE) { ptr += asgml2_poly_buf((LWPOLY*)subgeom, 0, ptr, precision, prefix); } else if (lwgeom_is_collection(subgeom)) { if (subgeom->type == COLLECTIONTYPE) ptr += asgml2_collection_buf((LWCOLLECTION*)subgeom, 0, ptr, precision, prefix); else ptr += asgml2_multi_buf((LWCOLLECTION*)subgeom, 0, ptr, precision, prefix); } ptr += snprintf(ptr, strlen(ptr), "", prefix); } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml2_collection(const LWCOLLECTION *col, const char *srs, int precision, const char *prefix) { char *gml; size_t size; size = asgml2_collection_size(col, srs, precision, prefix); gml = lwalloc(size); asgml2_collection_buf(col, srs, gml, precision, prefix); return gml; } static size_t pointArray_toGML2(POINTARRAY *pa, char *output, int precision) { uint32_t i; char *ptr; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; char z[OUT_DOUBLE_BUFFER_SIZE]; ptr = output; if ( ! FLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { const POINT2D *pt; pt = getPoint2d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), " "); ptr += snprintf(ptr, strlen(ptr), "%s,%s", x, y); } } else { for (i=0; inpoints; i++) { const POINT3D *pt = getPoint3d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->z, precision, z, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), " "); ptr += snprintf(ptr, strlen(ptr), "%s,%s,%s", x, y, z); } } return ptr-output; } /* * VERSION GML 3.1.1 */ /* takes a GEOMETRY and returns a GML representation */ extern char * lwgeom_to_gml3(const LWGEOM *geom, const char *srs, int precision, int opts, const char *prefix, const char *id) { int type = geom->type; /* Return null for empty (#1377) */ if ( lwgeom_is_empty(geom) ) return NULL; switch (type) { case POINTTYPE: return asgml3_point((LWPOINT*)geom, srs, precision, opts, prefix, id); case LINETYPE: return asgml3_line((LWLINE*)geom, srs, precision, opts, prefix, id); case CIRCSTRINGTYPE: return asgml3_circstring((LWCIRCSTRING*)geom, srs, precision, opts, prefix, id ); case POLYGONTYPE: return asgml3_poly((LWPOLY*)geom, srs, precision, opts, 0, prefix, id); case CURVEPOLYTYPE: return asgml3_curvepoly((LWCURVEPOLY*)geom, srs, precision, opts, prefix, id); case TRIANGLETYPE: return asgml3_triangle((LWTRIANGLE*)geom, srs, precision, opts, prefix, id); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: return asgml3_multi((LWCOLLECTION*)geom, srs, precision, opts, prefix, id); case POLYHEDRALSURFACETYPE: return asgml3_psurface((LWPSURFACE*)geom, srs, precision, opts, prefix, id); case TINTYPE: return asgml3_tin((LWTIN*)geom, srs, precision, opts, prefix, id); case COLLECTIONTYPE: return asgml3_collection((LWCOLLECTION*)geom, srs, precision, opts, prefix, id); case COMPOUNDTYPE: return asgml3_compound( (LWCOMPOUND*)geom, srs, precision, opts, prefix, id ); case MULTICURVETYPE: return asgml3_multicurve( (LWMCURVE*)geom, srs, precision, opts, prefix, id ); case MULTISURFACETYPE: return asgml3_multisurface( (LWMSURFACE*)geom, srs, precision, opts, prefix, id ); default: lwerror("lwgeom_to_gml3: '%s' geometry type not supported", lwtype_name(type)); return NULL; } } static size_t asgml3_point_size(const LWPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(point->point, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_point_buf(const LWPOINT *point, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr = output; int dimension=2; if (FLAGS_GET_Z(point->flags)) dimension = 3; ptr += snprintf(ptr, strlen(ptr), "<%sPoint", prefix); if ( srs ) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if ( id ) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); if ( lwpoint_is_empty(point) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); if (IS_DIMS(opts)) ptr += snprintf(ptr, strlen(ptr), "<%spos srsDimension=\"%d\">", prefix, dimension); else ptr += snprintf(ptr, strlen(ptr), "<%spos>", prefix); ptr += pointArray_toGML3(point->point, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); return (ptr-output); } static char * asgml3_point(const LWPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_point_size(point, srs, precision, opts, prefix, id); output = lwalloc(size); asgml3_point_buf(point, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_line_size(const LWLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(line->points, precision); if ( opts & LW_GML_SHORTLINE ) { size += ( sizeof("/") + ( prefixlen * 2 ) ) * 2; } else { size += ( sizeof("/") + ( prefixlen * 4 ) ) * 2; } if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_line_buf(const LWLINE *line, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr=output; int dimension=2; int shortline = ( opts & LW_GML_SHORTLINE ); if (FLAGS_GET_Z(line->flags)) dimension = 3; if ( shortline ) { ptr += snprintf(ptr, strlen(ptr), "<%sLineString", prefix); } else { ptr += snprintf(ptr, strlen(ptr), "<%sCurve", prefix); } if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); if ( lwline_is_empty(line) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); if ( ! shortline ) { ptr += snprintf(ptr, strlen(ptr), "<%ssegments>", prefix); ptr += snprintf(ptr, strlen(ptr), "<%sLineStringSegment>", prefix); } if (IS_DIMS(opts)) { ptr += snprintf(ptr, strlen(ptr), "<%sposList srsDimension=\"%d\">", prefix, dimension); } else { ptr += snprintf(ptr, strlen(ptr), "<%sposList>", prefix); } ptr += pointArray_toGML3(line->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix); if ( shortline ) { ptr += snprintf(ptr, strlen(ptr), "", prefix); } else { ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); } return (ptr-output); } static char * asgml3_line(const LWLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_line_size(line, srs, precision, opts, prefix, id); output = lwalloc(size); asgml3_line_buf(line, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_circstring_size(const LWCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size = pointArray_GMLsize( circ->points, precision ); size_t prefixlen = strlen(prefix); size += 2 * ( sizeof( "/" ) + 2 * prefixlen ); size += 2 * ( sizeof( "/" ) + 2 * prefixlen ); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_circstring_buf(const LWCIRCSTRING *circ, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char* ptr = output; int dimension=2; if (FLAGS_GET_Z(circ->flags)) { dimension = 3; } ptr += snprintf(ptr, strlen(ptr), "<%sCurve", prefix); if (srs) { ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); } if (id) { ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%ssegments>", prefix); ptr += snprintf(ptr, strlen(ptr), "<%sArcString>", prefix); ptr += snprintf(ptr, strlen(ptr), "<%sposList", prefix); if (IS_DIMS(opts)) { ptr += snprintf(ptr, strlen(ptr), " srsDimension=\"%d\"", dimension); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += pointArray_toGML3(circ->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } static char * asgml3_circstring( const LWCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char *output; int size; size = asgml3_circstring_size(circ, srs, precision, opts, prefix, id); output = lwalloc( size ); asgml3_circstring_buf(circ, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_poly_size(const LWPOLY *poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t size; size_t prefixlen = strlen(prefix); uint32_t i; size = ( sizeof("///") + (prefixlen*3) ) * 2; size += ( sizeof("//") + (prefixlen*2) ) * 2 * (poly->nrings - 1); size += ( sizeof("") + (prefixlen*2) ) * poly->nrings; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'") * poly->nrings; for (i=0; inrings; i++) size += pointArray_GMLsize(poly->rings[i], precision); return size; } static size_t asgml3_poly_buf(const LWPOLY *poly, const char *srs, char *output, int precision, int opts, int is_patch, const char *prefix, const char *id) { uint32_t i; char *ptr=output; int dimension=2; if (FLAGS_GET_Z(poly->flags)) dimension = 3; if (is_patch) { ptr += snprintf(ptr, strlen(ptr), "<%sPolygonPatch", prefix); } else { ptr += snprintf(ptr, strlen(ptr), "<%sPolygon", prefix); } if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); if ( lwpoly_is_empty(poly) ) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%sexterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += snprintf(ptr, strlen(ptr), "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += snprintf(ptr, strlen(ptr), "<%sposList>", prefix); ptr += pointArray_toGML3(poly->rings[0], ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix, prefix); for (i=1; inrings; i++) { ptr += snprintf(ptr, strlen(ptr), "<%sinterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += snprintf(ptr, strlen(ptr), "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += snprintf(ptr, strlen(ptr), "<%sposList>", prefix); ptr += pointArray_toGML3(poly->rings[i], ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix, prefix); } if (is_patch) ptr += snprintf(ptr, strlen(ptr), "", prefix); else ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } static char * asgml3_poly(const LWPOLY *poly, const char *srs, int precision, int opts, int is_patch, const char *prefix, const char *id) { char *output; int size; size = asgml3_poly_size(poly, srs, precision, opts, prefix, id); output = lwalloc(size); asgml3_poly_buf(poly, srs, output, precision, opts, is_patch, prefix, id); return output; } static size_t asgml3_compound_size(const LWCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ) { uint32_t i; size_t size; LWGEOM *subgeom; size_t prefixlen = strlen(prefix); size = ( sizeof( "" ) + 2 * prefixlen ); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); size += ( sizeof("") + 2 * prefixlen ); for(i= 0; i < col->ngeoms; ++i ) { subgeom = col->geoms[i]; if ( subgeom->type == LINETYPE ) { size += sizeof( "points, precision ); } else if( subgeom->type == CIRCSTRINGTYPE ) { size += sizeof( "") + 4 * prefixlen; size += pointArray_GMLsize( ((LWCIRCSTRING*)subgeom)->points, precision ); } else { continue; } if (IS_DIMS(opts)) { size += sizeof(" srsDimension='x'"); } } return size; } static size_t asgml3_compound_buf(const LWCOMPOUND *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { LWGEOM *subgeom; uint32_t i; char* ptr = output; int dimension=2; if (FLAGS_GET_Z(col->flags)) { dimension = 3; } ptr += snprintf(ptr, strlen(ptr), "<%sCurve", prefix ); if (srs) { ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); } if (id) { ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id ); } ptr += snprintf(ptr, strlen(ptr), ">" ); ptr += snprintf(ptr, strlen(ptr), "<%ssegments>", prefix ); for( i = 0; i < col->ngeoms; ++i ) { subgeom = col->geoms[i]; if( subgeom->type != LINETYPE && subgeom->type != CIRCSTRINGTYPE ) { continue; } if ( subgeom->type == LINETYPE ) { ptr += snprintf(ptr, strlen(ptr), "<%sLineStringSegment><%sposList", prefix, prefix ); if (IS_DIMS(opts)) { ptr += snprintf(ptr, strlen(ptr), " srsDimension=\"%d\"", dimension); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += pointArray_toGML3(((LWCIRCSTRING*)subgeom)->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix ); } else if( subgeom->type == CIRCSTRINGTYPE ) { ptr += snprintf(ptr, strlen(ptr), "<%sArcString><%sposList" , prefix, prefix ); if (IS_DIMS(opts)) { ptr += snprintf(ptr, strlen(ptr), " srsDimension=\"%d\"", dimension); } ptr += snprintf(ptr, strlen(ptr), ">"); ptr += pointArray_toGML3(((LWLINE*)subgeom)->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix ); } } ptr += snprintf(ptr, strlen(ptr), "", prefix ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); return ( ptr - output ); } static char * asgml3_compound(const LWCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char* gml; size_t size; size = asgml3_compound_size( col, srs, precision, opts, prefix, id ); gml = lwalloc( size ); asgml3_compound_buf( col, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_curvepoly_size(const LWCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t prefixlen = strlen(prefix); LWGEOM* subgeom; size_t size = sizeof( "nrings; ++i ) { if( i == 0 ) { size += sizeof( "" ) + 2 * prefixlen; } else { size += sizeof( "" ) + 2 * prefixlen; } subgeom = poly->rings[i]; if ( subgeom->type == LINETYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("points, precision ); } else if( subgeom->type == CIRCSTRINGTYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("") + 2 * prefixlen; size += asgml3_circstring_size((LWCIRCSTRING*)subgeom, srs, precision, opts, prefix, id); } else if( subgeom->type == COMPOUNDTYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("") + 2 * prefixlen; size += asgml3_compound_size( (LWCOMPOUND*)subgeom, srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_curvepoly_buf(const LWCURVEPOLY* poly, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { uint32_t i; LWGEOM* subgeom; char *ptr=output; int dimension=2; if (FLAGS_GET_Z(poly->flags)) { dimension = 3; } ptr += snprintf(ptr, strlen(ptr), "<%sPolygon", prefix ); if (srs) { ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); } if (id) { ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id ); } ptr += snprintf(ptr, strlen(ptr), ">"); for( i = 0; i < poly->nrings; ++i ) { if( i == 0 ) { ptr += snprintf(ptr, strlen(ptr), "<%sexterior>", prefix); } else { ptr += snprintf(ptr, strlen(ptr), "<%sinterior>", prefix); } subgeom = poly->rings[i]; if ( subgeom->type == LINETYPE ) { ptr += snprintf(ptr, strlen(ptr), "<%sLinearRing>", prefix ); ptr += snprintf(ptr, strlen(ptr), "<%sposList", prefix ); if (IS_DIMS(opts)) { ptr += snprintf(ptr, strlen(ptr), " srsDimension=\"%d\"", dimension); } ptr += snprintf(ptr, strlen(ptr), ">" ); ptr += pointArray_toGML3(((LWLINE*)subgeom)->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); } else if( subgeom->type == CIRCSTRINGTYPE ) { ptr += snprintf(ptr, strlen(ptr), "<%sRing>", prefix ); ptr += snprintf(ptr, strlen(ptr), "<%scurveMember>", prefix ); ptr += asgml3_circstring_buf( (LWCIRCSTRING*)subgeom, srs, ptr, precision, opts, prefix, id ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); } else if( subgeom->type == COMPOUNDTYPE ) { ptr += snprintf(ptr, strlen(ptr), "<%sRing>", prefix ); ptr += snprintf(ptr, strlen(ptr), "<%scurveMember>", prefix ); ptr += asgml3_compound_buf( (LWCOMPOUND*)subgeom, srs, ptr, precision, opts, prefix, id ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); ptr += snprintf(ptr, strlen(ptr), "", prefix ); } if( i == 0 ) { ptr += snprintf(ptr, strlen(ptr), "", prefix); } else { ptr += snprintf(ptr, strlen(ptr), "", prefix); } } ptr += snprintf(ptr, strlen(ptr), "", prefix ); return (ptr - output); } static char* asgml3_curvepoly(const LWCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { char* gml; size_t size; size = asgml3_curvepoly_size( poly, srs, precision, opts, prefix, id ); gml = lwalloc( size ); asgml3_curvepoly_buf( poly, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_triangle_size(const LWTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t size; size_t prefixlen = strlen(prefix); size = ( sizeof("///") + (prefixlen*3) ) * 2; size += sizeof("") + (prefixlen*2); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(prefix) + strlen(id) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); size += pointArray_GMLsize(triangle->points, precision); return size; } static size_t asgml3_triangle_buf(const LWTRIANGLE *triangle, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr=output; int dimension=2; if (FLAGS_GET_Z(triangle->flags)) dimension = 3; ptr += snprintf(ptr, strlen(ptr), "<%sTriangle", prefix); if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); ptr += snprintf(ptr, strlen(ptr), ">"); ptr += snprintf(ptr, strlen(ptr), "<%sexterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += snprintf(ptr, strlen(ptr), "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += snprintf(ptr, strlen(ptr), "<%sposList>", prefix); ptr += pointArray_toGML3(triangle->points, ptr, precision, opts); ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix, prefix); ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } static char * asgml3_triangle(const LWTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_triangle_size(triangle, srs, precision, opts, prefix, id); output = lwalloc(size); asgml3_triangle_buf(triangle, srs, output, precision, opts, prefix, id); return output; } /* * Compute max size required for GML version of this * inspected geometry. Will recurse when needed. * Don't call this with single-geoms inspected. */ static size_t asgml3_multi_size(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); LWGEOM *subgeom; /* the longest possible multi version */ size = sizeof("") + prefixlen*2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == POINTTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_point_size((LWPOINT*)subgeom, 0, precision, opts, prefix, id); } else if (subgeom->type == LINETYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_line_size((LWLINE*)subgeom, 0, precision, opts, prefix, id); } else if (subgeom->type == POLYGONTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_poly_size((LWPOLY*)subgeom, 0, precision, opts, prefix, id); } } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_multi_buf(const LWCOLLECTION *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { int type = col->type; char *ptr, *gmltype; uint32_t i; LWGEOM *subgeom; ptr = output; gmltype=""; if (type == MULTIPOINTTYPE) gmltype = "MultiPoint"; else if (type == MULTILINETYPE) gmltype = "MultiCurve"; else if (type == MULTIPOLYGONTYPE) gmltype = "MultiSurface"; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%s%s", prefix, gmltype); if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); if (!col->ngeoms) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == POINTTYPE) { ptr += snprintf(ptr, strlen(ptr), "<%spointMember>", prefix); ptr += asgml3_point_buf((LWPOINT*)subgeom, 0, ptr, precision, opts, prefix, id); ptr += snprintf(ptr, strlen(ptr), "", prefix); } else if (subgeom->type == LINETYPE) { ptr += snprintf(ptr, strlen(ptr), "<%scurveMember>", prefix); ptr += asgml3_line_buf((LWLINE*)subgeom, 0, ptr, precision, opts, prefix, id); ptr += snprintf(ptr, strlen(ptr), "", prefix); } else if (subgeom->type == POLYGONTYPE) { ptr += snprintf(ptr, strlen(ptr), "<%ssurfaceMember>", prefix); ptr += asgml3_poly_buf((LWPOLY*)subgeom, 0, ptr, precision, opts, 0, prefix, id); ptr += snprintf(ptr, strlen(ptr), "", prefix); } } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix, gmltype); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_multi(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_multi_size(col, srs, precision, opts, prefix, id); gml = lwalloc(size); asgml3_multi_buf(col, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_psurface_size(const LWPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); size = (sizeof("/") + prefixlen*2) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { size += asgml3_poly_size(psur->geoms[i], 0, precision, opts, prefix, id); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_psurface_buf(const LWPSURFACE *psur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; uint32_t i; ptr = output; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%sPolyhedralSurface", prefix); if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); ptr += snprintf(ptr, strlen(ptr), "><%spolygonPatches>", prefix); for (i=0; ingeoms; i++) { ptr += asgml3_poly_buf(psur->geoms[i], 0, ptr, precision, opts, 1, prefix, id); } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_psurface(const LWPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_psurface_size(psur, srs, precision, opts, prefix, id); gml = lwalloc(size); asgml3_psurface_buf(psur, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_tin_size(const LWTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); size = (sizeof("/") + prefixlen*2) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { size += asgml3_triangle_size(tin->geoms[i], 0, precision, opts, prefix, id); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_tin_buf(const LWTIN *tin, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; uint32_t i; ptr = output; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%sTin", prefix); if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); else ptr += snprintf(ptr, strlen(ptr), "><%strianglePatches>", prefix); for (i=0; ingeoms; i++) { ptr += asgml3_triangle_buf(tin->geoms[i], 0, ptr, precision, opts, prefix, id); } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix, prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_tin(const LWTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_tin_size(tin, srs, precision, opts, prefix, id); gml = lwalloc(size); asgml3_tin_buf(tin, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_collection_size(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { uint32_t i; size_t size; size_t prefixlen = strlen(prefix); LWGEOM *subgeom; size = sizeof("") + prefixlen*2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += ( sizeof("/") + prefixlen ) * 2; if ( subgeom->type == POINTTYPE ) { size += asgml3_point_size((LWPOINT*)subgeom, 0, precision, opts, prefix, id); } else if ( subgeom->type == LINETYPE ) { size += asgml3_line_size((LWLINE*)subgeom, 0, precision, opts, prefix, id); } else if ( subgeom->type == POLYGONTYPE ) { size += asgml3_poly_size((LWPOLY*)subgeom, 0, precision, opts, prefix, id); } else if ( lwgeom_is_collection(subgeom) ) { size += asgml3_multi_size((LWCOLLECTION*)subgeom, 0, precision, opts, prefix, id); } else lwerror("asgml3_collection_size: unknown geometry type"); } return size; } static size_t asgml3_collection_buf(const LWCOLLECTION *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; uint32_t i; LWGEOM *subgeom; ptr = output; /* Open outmost tag */ ptr += snprintf(ptr, strlen(ptr), "<%sMultiGeometry", prefix); if (srs) ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); if (id) ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id); if (!col->ngeoms) { ptr += snprintf(ptr, strlen(ptr), "/>"); return (ptr-output); } ptr += snprintf(ptr, strlen(ptr), ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; ptr += snprintf(ptr, strlen(ptr), "<%sgeometryMember>", prefix); if ( subgeom->type == POINTTYPE ) { ptr += asgml3_point_buf((LWPOINT*)subgeom, 0, ptr, precision, opts, prefix, id); } else if ( subgeom->type == LINETYPE ) { ptr += asgml3_line_buf((LWLINE*)subgeom, 0, ptr, precision, opts, prefix, id); } else if ( subgeom->type == POLYGONTYPE ) { ptr += asgml3_poly_buf((LWPOLY*)subgeom, 0, ptr, precision, opts, 0, prefix, id); } else if ( lwgeom_is_collection(subgeom) ) { if ( subgeom->type == COLLECTIONTYPE ) ptr += asgml3_collection_buf((LWCOLLECTION*)subgeom, 0, ptr, precision, opts, prefix, id); else ptr += asgml3_multi_buf((LWCOLLECTION*)subgeom, 0, ptr, precision, opts, prefix, id); } else lwerror("asgml3_collection_buf: unknown geometry type"); ptr += snprintf(ptr, strlen(ptr), "", prefix); } /* Close outmost tag */ ptr += snprintf(ptr, strlen(ptr), "", prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_collection(const LWCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_collection_size(col, srs, precision, opts, prefix, id); gml = lwalloc(size); asgml3_collection_buf(col, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_multicurve_size( const LWMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ) { size_t prefixlen = strlen(prefix); size_t size = sizeof( "" ) + 2 * prefixlen; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); LWGEOM* subgeom; uint32_t i; for( i = 0; i < cur->ngeoms; ++i ) { size += sizeof( "" ) + 2 * prefixlen; subgeom = cur->geoms[i]; if ( subgeom->type == LINETYPE ) { size += asgml3_line_size( (LWLINE*)subgeom, srs, precision, opts, prefix, id ); } else if( subgeom->type == CIRCSTRINGTYPE ) { size += asgml3_circstring_size( (LWCIRCSTRING*)subgeom, srs, precision, opts, prefix, id ); } else if( subgeom->type == COMPOUNDTYPE ) { size += asgml3_compound_size( (LWCOMPOUND*)subgeom, srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_multicurve_buf( const LWMCURVE* cur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id ) { char* ptr = output; LWGEOM* subgeom; uint32_t i; ptr += snprintf(ptr, strlen(ptr), "<%sMultiCurve", prefix ); if (srs) { ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); } if (id) { ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id ); } ptr += snprintf(ptr, strlen(ptr), ">"); for( i = 0; i < cur->ngeoms; ++i ) { ptr += snprintf(ptr, strlen(ptr), "<%scurveMember>", prefix ); subgeom = cur->geoms[i]; if ( subgeom->type == LINETYPE ) { ptr += asgml3_line_buf( (LWLINE*)subgeom, srs, ptr, precision, opts, prefix, id ); } else if( subgeom->type == CIRCSTRINGTYPE ) { ptr += asgml3_circstring_buf( (LWCIRCSTRING*)subgeom, srs, ptr, precision, opts, prefix, id ); } else if( subgeom->type == COMPOUNDTYPE ) { ptr += asgml3_compound_buf( (LWCOMPOUND*)subgeom, srs, ptr, precision, opts, prefix, id ); } ptr += snprintf(ptr, strlen(ptr), "", prefix ); } ptr += snprintf(ptr, strlen(ptr), "", prefix ); return (ptr - output); } static char *asgml3_multicurve( const LWMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char* gml; size_t size =asgml3_multicurve_size( cur, srs, precision, opts, prefix, id ); gml = lwalloc( size ); asgml3_multicurve_buf( cur, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_multisurface_size(const LWMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t prefixlen = strlen(prefix); size_t size = sizeof( "" ) + 2 * prefixlen; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); LWGEOM* subgeom; uint32_t i; for( i = 0; i < sur->ngeoms; ++i ) { subgeom = sur->geoms[i]; if( subgeom->type == POLYGONTYPE ) { size += asgml3_poly_size( (LWPOLY*)sur->geoms[i], srs, precision, opts, prefix, id ); } else if( subgeom->type == CURVEPOLYTYPE ) { size += asgml3_curvepoly_size( (LWCURVEPOLY*)sur->geoms[i], srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_multisurface_buf(const LWMSURFACE *sur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char* ptr = output; uint32_t i; LWGEOM* subgeom; ptr += snprintf(ptr, strlen(ptr), "<%sMultiSurface", prefix ); if (srs) { ptr += snprintf(ptr, strlen(ptr), " srsName=\"%s\"", srs); } if (id) { ptr += snprintf(ptr, strlen(ptr), " %sid=\"%s\"", prefix, id ); } ptr += snprintf(ptr, strlen(ptr), ">" ); for( i = 0; i < sur->ngeoms; ++i ) { subgeom = sur->geoms[i]; if( subgeom->type == POLYGONTYPE ) { ptr += asgml3_poly_buf( (LWPOLY*)sur->geoms[i], srs, ptr, precision, opts, 0, prefix, id ); } else if( subgeom->type == CURVEPOLYTYPE ) { ptr += asgml3_curvepoly_buf( (LWCURVEPOLY*)sur->geoms[i], srs, ptr, precision, opts, prefix, id ); } } ptr += snprintf(ptr, strlen(ptr), "", prefix ); return ptr - output; } static char *asgml3_multisurface(const LWMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id) { char* gml; size_t size = asgml3_multisurface_size( sur, srs, precision, opts, prefix, id ); gml = lwalloc( size ); asgml3_multisurface_buf( sur, srs, gml, precision, opts, prefix, id ); return gml; } /* In GML3, inside or , coordinates are separated by a space separator * In GML3 also, lat/lon are reversed for geocentric data */ static size_t pointArray_toGML3(POINTARRAY *pa, char *output, int precision, int opts) { uint32_t i; char *ptr; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; char z[OUT_DOUBLE_BUFFER_SIZE]; ptr = output; if ( ! FLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { const POINT2D *pt; pt = getPoint2d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), " "); if (IS_DEGREE(opts)) ptr += snprintf(ptr, strlen(ptr), "%s %s", y, x); else ptr += snprintf(ptr, strlen(ptr), "%s %s", x, y); } } else { for (i=0; inpoints; i++) { const POINT3D *pt = getPoint3d_cp(pa, i); lwprint_double( pt->x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->y, precision, y, OUT_DOUBLE_BUFFER_SIZE); lwprint_double( pt->z, precision, z, OUT_DOUBLE_BUFFER_SIZE); if ( i ) ptr += snprintf(ptr, strlen(ptr), " "); if (IS_DEGREE(opts)) ptr += snprintf(ptr, strlen(ptr), "%s %s %s", y, x, z); else ptr += snprintf(ptr, strlen(ptr), "%s %s %s", x, y, z); } } return ptr-output; } /* * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_GMLsize(POINTARRAY *pa, int precision) { if (FLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(", ")) * 2 * pa->npoints; return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(", ")) * 3 * pa->npoints; } lwgeom/src/liblwgeom/gserialized2.c0000644000176200001440000012445513773172540017070 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * Copyright 2017 Darafei Praliaskouski * **********************************************************************/ /* * GSERIALIZED verison 2 includes an optional extended flags uint64_t * before the optional bounding box. There may be other optional * components before the data area, but they all must be double * aligned to that the ordinates remain double aligned. * * size Used by PgSQL VARSIZE g->size * srid * gflags> 1 byte g->gflags * [ Optional extended flags (check flags for cue) * ] * [ Optional bounding box (check flags for cue) * Number of dimensions is variable * and also indicated in the flags * ] * ... * data area */ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "lwgeodetic.h" #include "gserialized2.h" /*********************************************************************** * GSERIALIZED metadata utility functions. */ static int gserialized2_read_gbox_p(const GSERIALIZED *g, GBOX *gbox); lwflags_t gserialized2_get_lwflags(const GSERIALIZED *g) { lwflags_t lwflags = 0; uint8_t gflags = g->gflags; FLAGS_SET_Z(lwflags, G2FLAGS_GET_Z(gflags)); FLAGS_SET_M(lwflags, G2FLAGS_GET_M(gflags)); FLAGS_SET_BBOX(lwflags, G2FLAGS_GET_BBOX(gflags)); FLAGS_SET_GEODETIC(lwflags, G2FLAGS_GET_GEODETIC(gflags)); if (G2FLAGS_GET_EXTENDED(gflags)) { uint64_t xflags = 0; memcpy(&xflags, g->data, sizeof(uint64_t)); FLAGS_SET_SOLID(lwflags, xflags & G2FLAG_X_SOLID); } return lwflags; } static int lwflags_uses_extended_flags(lwflags_t lwflags) { lwflags_t core_lwflags = LWFLAG_Z | LWFLAG_M | LWFLAG_BBOX | LWFLAG_GEODETIC; return (lwflags & (~core_lwflags)) != 0; } static inline size_t gserialized2_box_size(const GSERIALIZED *g) { if (G2FLAGS_GET_GEODETIC(g->gflags)) return 6 * sizeof(float); else return 2 * G2FLAGS_NDIMS(g->gflags) * sizeof(float); } static inline size_t gserialized2_header_size(const GSERIALIZED *g) { uint32_t sz = 8; /* varsize (4) + srid(3) + flags (1) */ if (gserialized2_has_extended(g)) sz += 8; if (gserialized2_has_bbox(g)) sz += gserialized2_box_size(g); return sz; } /* Returns a pointer to the start of the geometry data */ static inline uint8_t * gserialized2_get_geometry_p(const GSERIALIZED *g) { uint32_t extra_data_bytes = 0; if (gserialized2_has_extended(g)) extra_data_bytes += sizeof(uint64_t); if (gserialized2_has_bbox(g)) extra_data_bytes += gserialized2_box_size(g); return ((uint8_t *)g->data) + extra_data_bytes; } uint8_t lwflags_get_g2flags(lwflags_t lwflags) { uint8_t gflags = 0; G2FLAGS_SET_Z(gflags, FLAGS_GET_Z(lwflags)); G2FLAGS_SET_M(gflags, FLAGS_GET_M(lwflags)); G2FLAGS_SET_BBOX(gflags, FLAGS_GET_BBOX(lwflags)); G2FLAGS_SET_GEODETIC(gflags, FLAGS_GET_GEODETIC(lwflags)); G2FLAGS_SET_EXTENDED(gflags, lwflags_uses_extended_flags(lwflags)); G2FLAGS_SET_VERSION(gflags, 1); return gflags; } /* handle missaligned uint32_t data */ static inline uint32_t gserialized2_get_uint32_t(const uint8_t *loc) { return *((uint32_t*)loc); } uint8_t g2flags(int has_z, int has_m, int is_geodetic) { uint8_t gflags = 0; if (has_z) G2FLAGS_SET_Z(gflags, 1); if (has_m) G2FLAGS_SET_M(gflags, 1); if (is_geodetic) G2FLAGS_SET_GEODETIC(gflags, 1); return gflags; } int gserialized2_has_bbox(const GSERIALIZED *g) { return G2FLAGS_GET_BBOX(g->gflags); } int gserialized2_has_extended(const GSERIALIZED *g) { return G2FLAGS_GET_EXTENDED(g->gflags); } int gserialized2_has_z(const GSERIALIZED *g) { return G2FLAGS_GET_Z(g->gflags); } int gserialized2_has_m(const GSERIALIZED *g) { return G2FLAGS_GET_M(g->gflags); } int gserialized2_ndims(const GSERIALIZED *g) { return G2FLAGS_NDIMS(g->gflags); } int gserialized2_is_geodetic(const GSERIALIZED *g) { return G2FLAGS_GET_GEODETIC(g->gflags); } uint32_t gserialized2_max_header_size(void) { static const intptr_t size_of_gserialized_up_to_data = (intptr_t) & ((GSERIALIZED *)NULL)->data; /* GSERIALIZED size + max bbox according gbox_serialized_size (XYZM*2) + extended flags + type */ return size_of_gserialized_up_to_data + 8 * sizeof(float) + sizeof(uint64_t) + sizeof(uint32_t); } uint32_t gserialized2_get_type(const GSERIALIZED *g) { uint8_t *ptr = gserialized2_get_geometry_p(g); return *((uint32_t*)(ptr)); } int32_t gserialized2_get_srid(const GSERIALIZED *g) { int32_t srid = 0; srid = srid | (g->srid[0] << 16); srid = srid | (g->srid[1] << 8); srid = srid | (g->srid[2]); /* Only the first 21 bits are set. Slide up and back to pull the negative bits down, if we need them. */ srid = (srid<<11)>>11; /* 0 is our internal unknown value. We'll map back and forth here for now */ if (srid == 0) return SRID_UNKNOWN; else return srid; } void gserialized2_set_srid(GSERIALIZED *g, int32_t srid) { LWDEBUGF(3, "%s called with srid = %d", __func__, srid); srid = clamp_srid(srid); /* 0 is our internal unknown value. * We'll map back and forth here for now */ if (srid == SRID_UNKNOWN) srid = 0; g->srid[0] = (srid & 0x001F0000) >> 16; g->srid[1] = (srid & 0x0000FF00) >> 8; g->srid[2] = (srid & 0x000000FF); } static size_t gserialized2_is_empty_recurse(const uint8_t *p, int *isempty); static size_t gserialized2_is_empty_recurse(const uint8_t *p, int *isempty) { int i; int32_t type, num; memcpy(&type, p, 4); memcpy(&num, p+4, 4); if (lwtype_is_collection(type)) { size_t lz = 8; for ( i = 0; i < num; i++ ) { lz += gserialized2_is_empty_recurse(p+lz, isempty); if (!*isempty) return lz; } *isempty = LW_TRUE; return lz; } else { *isempty = (num == 0 ? LW_TRUE : LW_FALSE); return 8; } } int gserialized2_is_empty(const GSERIALIZED *g) { int isempty = 0; uint8_t *p = gserialized2_get_geometry_p(g); gserialized2_is_empty_recurse(p, &isempty); return isempty; } /* Prototype for lookup3.c */ /* key = the key to hash */ /* length = length of the key */ /* pc = IN: primary initval, OUT: primary hash */ /* pb = IN: secondary initval, OUT: secondary hash */ void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); int32_t gserialized2_hash(const GSERIALIZED *g1) { int32_t hval; int32_t pb = 0, pc = 0; /* Point to just the type/coordinate part of buffer */ size_t hsz1 = gserialized2_header_size(g1); uint8_t *b1 = (uint8_t *)g1 + hsz1; /* Calculate size of type/coordinate buffer */ size_t sz1 = SIZE_GET(g1->size); size_t bsz1 = sz1 - hsz1; /* Calculate size of srid/type/coordinate buffer */ int32_t srid = gserialized2_get_srid(g1); size_t bsz2 = bsz1 + sizeof(int); uint8_t *b2 = lwalloc(bsz2); /* Copy srid into front of combined buffer */ memcpy(b2, &srid, sizeof(int)); /* Copy type/coordinates into rest of combined buffer */ memcpy(b2+sizeof(int), b1, bsz1); /* Hash combined buffer */ hashlittle2(b2, bsz2, (uint32_t *)&pb, (uint32_t *)&pc); lwfree(b2); hval = pb ^ pc; return hval; } const float * gserialized2_get_float_box_p(const GSERIALIZED *g, size_t *ndims) { uint8_t *ptr = (uint8_t*)(g->data); size_t bndims = G2FLAGS_NDIMS_BOX(g->gflags); if (ndims) *ndims = bndims; /* Cannot do anything if there's no box */ if (!(g && gserialized_has_bbox(g))) return NULL; /* Advance past optional extended flags */ if (gserialized2_has_extended(g)) ptr += 8; return (const float *)(ptr); } int gserialized2_read_gbox_p(const GSERIALIZED *g, GBOX *gbox) { uint8_t gflags = g->gflags; /* Null input! */ if (!(g && gbox)) return LW_FAILURE; /* Initialize the flags on the box */ gbox->flags = gserialized2_get_lwflags(g); /* Has pre-calculated box */ if (G2FLAGS_GET_BBOX(gflags)) { int i = 0; const float *fbox = gserialized2_get_float_box_p(g, NULL); gbox->xmin = fbox[i++]; gbox->xmax = fbox[i++]; gbox->ymin = fbox[i++]; gbox->ymax = fbox[i++]; /* Geodetic? Read next dimension (geocentric Z) and return */ if (G2FLAGS_GET_GEODETIC(gflags)) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; return LW_SUCCESS; } /* Cartesian? Read extra dimensions (if there) and return */ if (G2FLAGS_GET_Z(gflags)) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; } if (G2FLAGS_GET_M(gflags)) { gbox->mmin = fbox[i++]; gbox->mmax = fbox[i++]; } return LW_SUCCESS; } return LW_FAILURE; } /* * Populate a bounding box *without* allocating an LWGEOM. Useful * for some performance purposes. */ int gserialized2_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) { uint32_t type = gserialized2_get_type(g); uint8_t *geometry_start = gserialized2_get_geometry_p(g); double *dptr = (double *)(geometry_start); int32_t *iptr = (int32_t *)(geometry_start); /* Peeking doesn't help if you already have a box or are geodetic */ if (G2FLAGS_GET_GEODETIC(g->gflags) || G2FLAGS_GET_BBOX(g->gflags)) { return LW_FAILURE; } /* Boxes of points are easy peasy */ if (type == POINTTYPE) { int i = 1; /* Start past */ /* Read the npoints flag */ int isempty = (iptr[1] == 0); /* EMPTY point has no box */ if (isempty) return LW_FAILURE; gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; gbox->flags = gserialized2_get_lwflags(g); if (G2FLAGS_GET_Z(g->gflags)) { gbox->zmin = gbox->zmax = dptr[i++]; } if (G2FLAGS_GET_M(g->gflags)) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(gbox); return LW_SUCCESS; } /* We can calculate the box of a two-point cartesian line trivially */ else if (type == LINETYPE) { int ndims = G2FLAGS_NDIMS(g->gflags); int i = 0; /* Start at */ int npoints = iptr[1]; /* Read the npoints */ /* This only works with 2-point lines */ if (npoints != 2) return LW_FAILURE; /* Advance to X */ /* Past */ i++; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); gbox->flags = gserialized2_get_lwflags(g); if (G2FLAGS_GET_Z(g->gflags)) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if (G2FLAGS_GET_M(g->gflags)) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(gbox); return LW_SUCCESS; } /* We can also do single-entry multi-points */ else if (type == MULTIPOINTTYPE) { int i = 0; /* Start at */ int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; /* This only works with single-entry multipoints */ if (ngeoms != 1) return LW_FAILURE; /* Npoints is at */ npoints = iptr[3]; /* The check below is necessary because we can have a MULTIPOINT * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */ if (npoints != 1) return LW_FAILURE; /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; /* Read the doubles from the one point */ gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; gbox->flags = gserialized2_get_lwflags(g); if (G2FLAGS_GET_Z(g->gflags)) { gbox->zmin = gbox->zmax = dptr[i++]; } if (G2FLAGS_GET_M(g->gflags)) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(gbox); return LW_SUCCESS; } /* And we can do single-entry multi-lines with two vertices (!!!) */ else if (type == MULTILINETYPE) { int ndims = G2FLAGS_NDIMS(g->gflags); int i = 0; /* Start at */ int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; /* This only works with 1-line multilines */ if (ngeoms != 1) return LW_FAILURE; /* Npoints is at */ npoints = iptr[3]; if (npoints != 2) return LW_FAILURE; /* Advance to X */ /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); gbox->flags = gserialized2_get_lwflags(g); if (G2FLAGS_GET_Z(g->gflags)) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if (G2FLAGS_GET_M(g->gflags)) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(gbox); return LW_SUCCESS; } return LW_FAILURE; } static inline void gserialized2_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point) { uint8_t dim = 0; out_point->x = dptr[dim++]; out_point->y = dptr[dim++]; if (G2FLAGS_GET_Z(flags)) { out_point->z = dptr[dim++]; } if (G2FLAGS_GET_M(flags)) { out_point->m = dptr[dim]; } } int gserialized2_peek_first_point(const GSERIALIZED *g, POINT4D *out_point) { uint8_t *geometry_start = gserialized2_get_geometry_p(g); uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0; if (isEmpty) { return LW_FAILURE; } uint32_t type = (((uint32_t *)geometry_start)[0]); /* Setup double_array_start depending on the geometry type */ double *double_array_start = NULL; switch (type) { case (POINTTYPE): /* For points we only need to jump over the type and npoints 32b ints */ double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t)); break; default: lwerror("%s is currently not implemented for type %d", __func__, type); return LW_FAILURE; } gserialized2_copy_point(double_array_start, g->gflags, out_point); return LW_SUCCESS; } /** * Read the bounding box off a serialization and calculate one if * it is not already there. */ int gserialized2_get_gbox_p(const GSERIALIZED *g, GBOX *box) { /* Try to just read the serialized box. */ if (gserialized2_read_gbox_p(g, box) == LW_SUCCESS) { return LW_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an lwgeom */ else if (gserialized2_peek_gbox_p(g, box) == LW_SUCCESS) { return LW_SUCCESS; } /* Damn! Nothing for it but to create an lwgeom... */ /* See http://trac.osgeo.org/postgis/ticket/1023 */ else { LWGEOM *lwgeom = lwgeom_from_gserialized(g); int ret = lwgeom_calculate_gbox(lwgeom, box); gbox_float_round(box); lwgeom_free(lwgeom); return ret; } } /** * Read the bounding box off a serialization and fail if * it is not already there. */ int gserialized2_fast_gbox_p(const GSERIALIZED *g, GBOX *box) { /* Try to just read the serialized box. */ if (gserialized2_read_gbox_p(g, box) == LW_SUCCESS) { return LW_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an lwgeom */ else if (gserialized2_peek_gbox_p(g, box) == LW_SUCCESS) { return LW_SUCCESS; } else { return LW_FAILURE; } } /*********************************************************************** * Calculate the GSERIALIZED size for an LWGEOM. */ /* Private functions */ static size_t gserialized2_from_any_size(const LWGEOM *geom); /* Local prototype */ static size_t gserialized2_from_lwpoint_size(const LWPOINT *point) { size_t size = 4; /* Type number. */ assert(point); size += 4; /* Number of points (one or zero (empty)). */ size += point->point->npoints * FLAGS_NDIMS(point->flags) * sizeof(double); LWDEBUGF(3, "point size = %d", size); return size; } static size_t gserialized2_from_lwline_size(const LWLINE *line) { size_t size = 4; /* Type number. */ assert(line); size += 4; /* Number of points (zero => empty). */ size += line->points->npoints * FLAGS_NDIMS(line->flags) * sizeof(double); LWDEBUGF(3, "linestring size = %d", size); return size; } static size_t gserialized2_from_lwtriangle_size(const LWTRIANGLE *triangle) { size_t size = 4; /* Type number. */ assert(triangle); size += 4; /* Number of points (zero => empty). */ size += triangle->points->npoints * FLAGS_NDIMS(triangle->flags) * sizeof(double); LWDEBUGF(3, "triangle size = %d", size); return size; } static size_t gserialized2_from_lwpoly_size(const LWPOLY *poly) { size_t size = 4; /* Type number. */ uint32_t i = 0; assert(poly); size += 4; /* Number of rings (zero => empty). */ if (poly->nrings % 2) size += 4; /* Padding to double alignment. */ for (i = 0; i < poly->nrings; i++) { size += 4; /* Number of points in ring. */ size += poly->rings[i]->npoints * FLAGS_NDIMS(poly->flags) * sizeof(double); } LWDEBUGF(3, "polygon size = %d", size); return size; } static size_t gserialized2_from_lwcircstring_size(const LWCIRCSTRING *curve) { size_t size = 4; /* Type number. */ assert(curve); size += 4; /* Number of points (zero => empty). */ size += curve->points->npoints * FLAGS_NDIMS(curve->flags) * sizeof(double); LWDEBUGF(3, "circstring size = %d", size); return size; } static size_t gserialized2_from_lwcollection_size(const LWCOLLECTION *col) { size_t size = 4; /* Type number. */ uint32_t i = 0; assert(col); size += 4; /* Number of sub-geometries (zero => empty). */ for (i = 0; i < col->ngeoms; i++) { size_t subsize = gserialized2_from_any_size(col->geoms[i]); size += subsize; LWDEBUGF(3, "lwcollection subgeom(%d) size = %d", i, subsize); } LWDEBUGF(3, "lwcollection size = %d", size); return size; } static size_t gserialized2_from_any_size(const LWGEOM *geom) { LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type)); switch (geom->type) { case POINTTYPE: return gserialized2_from_lwpoint_size((LWPOINT *)geom); case LINETYPE: return gserialized2_from_lwline_size((LWLINE *)geom); case POLYGONTYPE: return gserialized2_from_lwpoly_size((LWPOLY *)geom); case TRIANGLETYPE: return gserialized2_from_lwtriangle_size((LWTRIANGLE *)geom); case CIRCSTRINGTYPE: return gserialized2_from_lwcircstring_size((LWCIRCSTRING *)geom); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return gserialized2_from_lwcollection_size((LWCOLLECTION *)geom); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } } /* Public function */ size_t gserialized2_from_lwgeom_size(const LWGEOM *geom) { size_t size = 8; /* Header overhead (varsize+flags+srid) */ assert(geom); /* Reserve space for extended flags */ if (lwflags_uses_extended_flags(geom->flags)) size += 8; /* Reserve space for bounding box */ if (geom->bbox) size += gbox_serialized_size(geom->flags); size += gserialized2_from_any_size(geom); LWDEBUGF(3, "%s size = %d", __func__, size); return size; } /*********************************************************************** * Serialize an LWGEOM into GSERIALIZED. */ /* Private functions */ static size_t gserialized2_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf); static size_t gserialized2_from_lwpoint(const LWPOINT *point, uint8_t *buf) { uint8_t *loc; int ptsize = ptarray_point_size(point->point); int type = POINTTYPE; assert(point); assert(buf); if (FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags)) lwerror("Dimensions mismatch in lwpoint"); LWDEBUGF(2, "%s (%p, %p) called", __func__, point, buf); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of points (0 => empty). */ memcpy(loc, &(point->point->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if (point->point->npoints > 0) { memcpy(loc, getPoint_internal(point->point, 0), ptsize); loc += ptsize; } return (size_t)(loc - buf); } static size_t gserialized2_from_lwline(const LWLINE *line, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = LINETYPE; assert(line); assert(buf); LWDEBUGF(2, "%s (%p, %p) called", __func__, line, buf); if (FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags)) lwerror("Dimensions mismatch in lwline"); ptsize = ptarray_point_size(line->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(line->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); LWDEBUGF(3, "%s added npoints (%d)", __func__, line->points->npoints); /* Copy in the ordinates. */ if (line->points->npoints > 0) { size = line->points->npoints * ptsize; memcpy(loc, getPoint_internal(line->points, 0), size); loc += size; } LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * line->points->npoints); return (size_t)(loc - buf); } static size_t gserialized2_from_lwpoly(const LWPOLY *poly, uint8_t *buf) { uint32_t i; uint8_t *loc; int ptsize; int type = POLYGONTYPE; assert(poly); assert(buf); LWDEBUGF(2, "%s called", __func__); ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the nrings. */ memcpy(loc, &(poly->nrings), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints per ring. */ for (i = 0; i < poly->nrings; i++) { memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Add in padding if necessary to remain double aligned. */ if (poly->nrings % 2) { memset(loc, 0, sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Copy in the ordinates. */ for (i = 0; i < poly->nrings; i++) { POINTARRAY *pa = poly->rings[i]; size_t pasize; if (FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags)) lwerror("Dimensions mismatch in lwpoly"); pasize = pa->npoints * ptsize; if ( pa->npoints > 0 ) memcpy(loc, getPoint_internal(pa, 0), pasize); loc += pasize; } return (size_t)(loc - buf); } static size_t gserialized2_from_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = TRIANGLETYPE; assert(triangle); assert(buf); LWDEBUGF(2, "%s (%p, %p) called", __func__, triangle, buf); if (FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags)) lwerror("Dimensions mismatch in lwtriangle"); ptsize = ptarray_point_size(triangle->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); LWDEBUGF(3, "%s added npoints (%d)", __func__, triangle->points->npoints); /* Copy in the ordinates. */ if (triangle->points->npoints > 0) { size = triangle->points->npoints * ptsize; memcpy(loc, getPoint_internal(triangle->points, 0), size); loc += size; } LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * triangle->points->npoints); return (size_t)(loc - buf); } static size_t gserialized2_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = CIRCSTRINGTYPE; assert(curve); assert(buf); if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags)) lwerror("Dimensions mismatch in lwcircstring"); ptsize = ptarray_point_size(curve->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &curve->points->npoints, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if (curve->points->npoints > 0) { size = curve->points->npoints * ptsize; memcpy(loc, getPoint_internal(curve->points, 0), size); loc += size; } return (size_t)(loc - buf); } static size_t gserialized2_from_lwcollection(const LWCOLLECTION *coll, uint8_t *buf) { size_t subsize = 0; uint8_t *loc; uint32_t i; int type; assert(coll); assert(buf); type = coll->type; loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of subgeoms. */ memcpy(loc, &coll->ngeoms, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Serialize subgeoms. */ for (i = 0; i < coll->ngeoms; i++) { if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags)) lwerror("Dimensions mismatch in lwcollection"); subsize = gserialized2_from_lwgeom_any(coll->geoms[i], loc); loc += subsize; } return (size_t)(loc - buf); } static size_t gserialized2_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf) { assert(geom); assert(buf); LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d", geom->type, lwtype_name(geom->type), FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags)); LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf); switch (geom->type) { case POINTTYPE: return gserialized2_from_lwpoint((LWPOINT *)geom, buf); case LINETYPE: return gserialized2_from_lwline((LWLINE *)geom, buf); case POLYGONTYPE: return gserialized2_from_lwpoly((LWPOLY *)geom, buf); case TRIANGLETYPE: return gserialized2_from_lwtriangle((LWTRIANGLE *)geom, buf); case CIRCSTRINGTYPE: return gserialized2_from_lwcircstring((LWCIRCSTRING *)geom, buf); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return gserialized2_from_lwcollection((LWCOLLECTION *)geom, buf); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } return 0; } static size_t gserialized2_from_extended_flags(lwflags_t lwflags, uint8_t *buf) { if (lwflags_uses_extended_flags(lwflags)) { uint64_t xflags = 0; if (FLAGS_GET_SOLID(lwflags)) xflags |= G2FLAG_X_SOLID; // G2FLAG_X_CHECKED_VALID // G2FLAG_X_IS_VALID // G2FLAG_X_HAS_HASH memcpy(buf, &xflags, sizeof(uint64_t)); return sizeof(uint64_t); } return 0; } static size_t gserialized2_from_gbox(const GBOX *gbox, uint8_t *buf) { uint8_t *loc = buf; float f; size_t return_size; assert(buf); f = next_float_down(gbox->xmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->xmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_down(gbox->ymin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->ymax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); if (FLAGS_GET_GEODETIC(gbox->flags)) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } if (FLAGS_GET_Z(gbox->flags)) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } if (FLAGS_GET_M(gbox->flags)) { f = next_float_down(gbox->mmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->mmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } /* Public function */ GSERIALIZED* gserialized2_from_lwgeom(LWGEOM *geom, size_t *size) { size_t expected_size = 0; size_t return_size = 0; uint8_t *ptr = NULL; GSERIALIZED *g = NULL; assert(geom); /* ** See if we need a bounding box, add one if we don't have one. */ if ((!geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom))) { lwgeom_add_bbox(geom); } /* ** Harmonize the flags to the state of the lwgeom */ FLAGS_SET_BBOX(geom->flags, (geom->bbox ? 1 : 0)); /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */ expected_size = gserialized2_from_lwgeom_size(geom); ptr = lwalloc(expected_size); g = (GSERIALIZED*)(ptr); /* Set the SRID! */ gserialized2_set_srid(g, geom->srid); /* ** We are aping PgSQL code here, PostGIS code should use ** VARSIZE to set this for real. */ SIZE_SET(g->size, expected_size); g->gflags = lwflags_get_g2flags(geom->flags); /* Move write head past size, srid and flags. */ ptr += 8; /* Write in the extended flags if necessary */ ptr += gserialized2_from_extended_flags(geom->flags, ptr); /* Write in the serialized form of the gbox, if necessary. */ if (geom->bbox) ptr += gserialized2_from_gbox(geom->bbox, ptr); /* Write in the serialized form of the geometry. */ ptr += gserialized2_from_lwgeom_any(geom, ptr); /* Calculate size as returned by data processing functions. */ return_size = ptr - (uint8_t*)g; if (expected_size != return_size) /* Uh oh! */ { lwerror("Return size (%lu) not equal to expected size (%lu)!", return_size, expected_size); return NULL; } if (size) /* Return the output size to the caller if necessary. */ *size = return_size; return g; } // xxxx continue reviewing extended flags content from here /*********************************************************************** * De-serialize GSERIALIZED into an LWGEOM. */ static LWGEOM* lwgeom_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size); static LWPOINT* lwpoint_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWPOINT *point; uint32_t npoints = 0; assert(data_ptr); point = (LWPOINT*)lwalloc(sizeof(LWPOINT)); point->srid = SRID_UNKNOWN; /* Default */ point->bbox = NULL; point->type = POINTTYPE; point->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if (npoints > 0) point->point = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 1, data_ptr); else point->point = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty point */ data_ptr += npoints * FLAGS_NDIMS(lwflags) * sizeof(double); if (size) *size = data_ptr - start_ptr; return point; } static LWLINE* lwline_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWLINE *line; uint32_t npoints = 0; assert(data_ptr); line = (LWLINE*)lwalloc(sizeof(LWLINE)); line->srid = SRID_UNKNOWN; /* Default */ line->bbox = NULL; line->type = LINETYPE; line->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if (npoints > 0) line->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else line->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty linestring */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if (size) *size = data_ptr - start_ptr; return line; } static LWPOLY* lwpoly_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWPOLY *poly; uint8_t *ordinate_ptr; uint32_t nrings = 0; uint32_t i = 0; assert(data_ptr); poly = (LWPOLY*)lwalloc(sizeof(LWPOLY)); poly->srid = SRID_UNKNOWN; /* Default */ poly->bbox = NULL; poly->type = POLYGONTYPE; poly->flags = lwflags; data_ptr += 4; /* Skip past the polygontype. */ nrings = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */ poly->nrings = nrings; LWDEBUGF(4, "nrings = %d", nrings); data_ptr += 4; /* Skip past the nrings. */ ordinate_ptr = data_ptr; /* Start the ordinate pointer. */ if (nrings > 0) { poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings ); poly->maxrings = nrings; ordinate_ptr += nrings * 4; /* Move past all the npoints values. */ if (nrings % 2) /* If there is padding, move past that too. */ ordinate_ptr += 4; } else /* Empty polygon */ { poly->rings = NULL; poly->maxrings = 0; } for (i = 0; i < nrings; i++) { uint32_t npoints = 0; /* Read in the number of points. */ npoints = gserialized2_get_uint32_t(data_ptr); data_ptr += 4; /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */ poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, ordinate_ptr); ordinate_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints; } if (size) *size = ordinate_ptr - start_ptr; return poly; } static LWTRIANGLE* lwtriangle_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWTRIANGLE *triangle; uint32_t npoints = 0; assert(data_ptr); triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE)); triangle->srid = SRID_UNKNOWN; /* Default */ triangle->bbox = NULL; triangle->type = TRIANGLETYPE; triangle->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if (npoints > 0) triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else triangle->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty triangle */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if (size) *size = data_ptr - start_ptr; return triangle; } static LWCIRCSTRING* lwcircstring_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWCIRCSTRING *circstring; uint32_t npoints = 0; assert(data_ptr); circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING)); circstring->srid = SRID_UNKNOWN; /* Default */ circstring->bbox = NULL; circstring->type = CIRCSTRINGTYPE; circstring->flags = lwflags; data_ptr += 4; /* Skip past the circstringtype. */ npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if (npoints > 0) circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else circstring->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty circularstring */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if (size) *size = data_ptr - start_ptr; return circstring; } static LWCOLLECTION* lwcollection_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint32_t type; uint8_t *start_ptr = data_ptr; LWCOLLECTION *collection; uint32_t ngeoms = 0; uint32_t i = 0; assert(data_ptr); type = gserialized2_get_uint32_t(data_ptr); data_ptr += 4; /* Skip past the type. */ collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION)); collection->srid = SRID_UNKNOWN; /* Default */ collection->bbox = NULL; collection->type = type; collection->flags = lwflags; ngeoms = gserialized2_get_uint32_t(data_ptr); collection->ngeoms = ngeoms; /* Zero => empty geometry */ data_ptr += 4; /* Skip past the ngeoms. */ if (ngeoms > 0) { collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); collection->maxgeoms = ngeoms; } else { collection->geoms = NULL; collection->maxgeoms = 0; } /* Sub-geometries are never de-serialized with boxes (#1254) */ FLAGS_SET_BBOX(lwflags, 0); for (i = 0; i < ngeoms; i++) { uint32_t subtype = gserialized2_get_uint32_t(data_ptr); size_t subsize = 0; if (!lwcollection_allows_subtype(type, subtype)) { lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type)); lwfree(collection); return NULL; } collection->geoms[i] = lwgeom_from_gserialized2_buffer(data_ptr, lwflags, &subsize); data_ptr += subsize; } if (size) *size = data_ptr - start_ptr; return collection; } LWGEOM* lwgeom_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *g_size) { uint32_t type; assert(data_ptr); type = gserialized2_get_uint32_t(data_ptr); LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type), FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), FLAGS_GET_GEODETIC(lwflags), FLAGS_GET_BBOX(lwflags)); switch (type) { case POINTTYPE: return (LWGEOM *)lwpoint_from_gserialized2_buffer(data_ptr, lwflags, g_size); case LINETYPE: return (LWGEOM *)lwline_from_gserialized2_buffer(data_ptr, lwflags, g_size); case CIRCSTRINGTYPE: return (LWGEOM *)lwcircstring_from_gserialized2_buffer(data_ptr, lwflags, g_size); case POLYGONTYPE: return (LWGEOM *)lwpoly_from_gserialized2_buffer(data_ptr, lwflags, g_size); case TRIANGLETYPE: return (LWGEOM *)lwtriangle_from_gserialized2_buffer(data_ptr, lwflags, g_size); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_from_gserialized2_buffer(data_ptr, lwflags, g_size); default: lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type)); return NULL; } } LWGEOM* lwgeom_from_gserialized2(const GSERIALIZED *g) { lwflags_t lwflags = 0; int32_t srid = 0; uint32_t lwtype = 0; uint8_t *data_ptr = NULL; LWGEOM *lwgeom = NULL; GBOX bbox; size_t size = 0; assert(g); srid = gserialized2_get_srid(g); lwtype = gserialized2_get_type(g); lwflags = gserialized2_get_lwflags(g); LWDEBUGF(4, "Got type %d (%s), srid=%d", lwtype, lwtype_name(lwtype), srid); data_ptr = (uint8_t*)g->data; /* Skip optional flags */ if (G2FLAGS_GET_EXTENDED(g->gflags)) { data_ptr += sizeof(uint64_t); } /* Skip over optional bounding box */ if (FLAGS_GET_BBOX(lwflags)) data_ptr += gbox_serialized_size(lwflags); lwgeom = lwgeom_from_gserialized2_buffer(data_ptr, lwflags, &size); if (!lwgeom) lwerror("%s: unable create geometry", __func__); /* Ooops! */ lwgeom->type = lwtype; lwgeom->flags = lwflags; if (gserialized2_read_gbox_p(g, &bbox) == LW_SUCCESS) { lwgeom->bbox = gbox_copy(&bbox); } else if (lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS)) { lwgeom->bbox = gbox_copy(&bbox); } else { lwgeom->bbox = NULL; } lwgeom_set_srid(lwgeom, srid); return lwgeom; } /** * Update the bounding box of a #GSERIALIZED, allocating a fresh one * if there is not enough space to just write the new box in. * WARNING if a new object needs to be created, the * input pointer will have to be freed by the caller! Check * to see if input == output. Returns null if there's a problem * like mismatched dimensions. */ GSERIALIZED* gserialized2_set_gbox(GSERIALIZED *g, GBOX *gbox) { int g_ndims = G2FLAGS_NDIMS_BOX(g->gflags); int box_ndims = FLAGS_NDIMS_BOX(gbox->flags); GSERIALIZED *g_out = NULL; size_t box_size = 2 * g_ndims * sizeof(float); float *fbox; int fbox_pos = 0; /* The dimensionality of the inputs has to match or we are SOL. */ if (g_ndims != box_ndims) { return NULL; } /* Serialized already has room for a box. */ if (G2FLAGS_GET_BBOX(g->gflags)) { g_out = g; } /* Serialized has no box. We need to allocate enough space for the old data plus the box, and leave a gap in the memory segment to write the new values into. */ else { size_t varsize_in = SIZE_GET(g->size); size_t varsize_out = varsize_in + box_size; uint8_t *ptr_out, *ptr_in, *ptr; g_out = lwalloc(varsize_out); ptr_out = (uint8_t*)g_out; ptr = ptr_in = (uint8_t*)g; /* Copy the head of g into place */ memcpy(ptr_out, ptr_in, 8); ptr_out += 8; ptr_in += 8; /* Optionally copy extended bit into place */ if (G2FLAGS_GET_EXTENDED(g->gflags)) { memcpy(ptr_out, ptr_in, 8); ptr_out += 8; ptr_in += 8; } /* Copy the body of g into place after leaving space for the box */ ptr_out += box_size; memcpy(ptr_out, ptr_in, varsize_in - (ptr_in - ptr)); G2FLAGS_SET_BBOX(g_out->gflags, 1); SIZE_SET(g_out->size, varsize_out); } /* Move bounds to nearest float values */ gbox_float_round(gbox); /* Now write the float box values into the memory segement */ fbox = (float*)(g_out->data); /* Copy in X/Y */ fbox[fbox_pos++] = gbox->xmin; fbox[fbox_pos++] = gbox->xmax; fbox[fbox_pos++] = gbox->ymin; fbox[fbox_pos++] = gbox->ymax; /* Optionally copy in higher dims */ if(gserialized2_has_z(g) || gserialized2_is_geodetic(g)) { fbox[fbox_pos++] = gbox->zmin; fbox[fbox_pos++] = gbox->zmax; } if(gserialized2_has_m(g) && ! gserialized2_is_geodetic(g)) { fbox[fbox_pos++] = gbox->mmin; fbox[fbox_pos++] = gbox->mmax; } return g_out; } /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED* gserialized2_drop_gbox(GSERIALIZED *g) { int g_ndims = G2FLAGS_NDIMS_BOX(g->gflags); size_t box_size = 2 * g_ndims * sizeof(float); size_t g_out_size = SIZE_GET(g->size) - box_size; GSERIALIZED *g_out = lwalloc(g_out_size); /* Copy the contents while omitting the box */ if (G2FLAGS_GET_BBOX(g->gflags)) { uint8_t *outptr = (uint8_t*)g_out; uint8_t *inptr = (uint8_t*)g; /* Copy the header (size+type) of g into place */ memcpy(outptr, inptr, 8); outptr += 8; inptr += 8; /* Copy extended flags, if there are any */ if (G2FLAGS_GET_EXTENDED(g->gflags)) { memcpy(outptr, inptr, 8); outptr += 8; inptr += 8; } /* Advance past box */ inptr += box_size; /* Copy parts after the box into place */ memcpy(outptr, inptr, g_out_size - 8); G2FLAGS_SET_BBOX(g_out->gflags, 0); SIZE_SET(g_out->size, g_out_size); } /* No box? Nothing to do but copy and return. */ else { memcpy(g_out, g, g_out_size); } return g_out; } lwgeom/src/liblwgeom/gserialized1.h0000644000176200001440000001251113773172540017061 0ustar liggesusers/** * Macros for manipulating the 'flags' byte. A uint8_t used as follows: * VVSRGBMZ * Version bit, followed by * Validty, Solid, ReadOnly, Geodetic, HasBBox, HasM and HasZ flags. */ #define G1FLAG_Z 0x01 #define G1FLAG_M 0x02 #define G1FLAG_BBOX 0x04 #define G1FLAG_GEODETIC 0x08 #define G1FLAG_READONLY 0x10 #define G1FLAG_SOLID 0x20 /* VERSION BITS 0x40 */ /* VERSION BITS 0x80 */ #define G1FLAGS_GET_Z(gflags) ((gflags) & G1FLAG_Z) #define G1FLAGS_GET_M(gflags) (((gflags) & G1FLAG_M)>>1) #define G1FLAGS_GET_BBOX(gflags) (((gflags) & G1FLAG_BBOX)>>2) #define G1FLAGS_GET_GEODETIC(gflags) (((gflags) & G1FLAG_GEODETIC)>>3) #define G1FLAGS_GET_SOLID(gflags) (((gflags) & G1FLAG_SOLID)>>5) #define G1FLAGS_SET_Z(gflags, value) ((gflags) = (value) ? ((gflags) | G1FLAG_Z) : ((gflags) & ~G1FLAG_Z)) #define G1FLAGS_SET_M(gflags, value) ((gflags) = (value) ? ((gflags) | G1FLAG_M) : ((gflags) & ~G1FLAG_M)) #define G1FLAGS_SET_BBOX(gflags, value) ((gflags) = (value) ? ((gflags) | G1FLAG_BBOX) : ((gflags) & ~G1FLAG_BBOX)) #define G1FLAGS_SET_GEODETIC(gflags, value) ((gflags) = (value) ? ((gflags) | G1FLAG_GEODETIC) : ((gflags) & ~G1FLAG_GEODETIC)) #define G1FLAGS_SET_SOLID(gflags, value) ((gflags) = (value) ? ((gflags) | G1FLAG_SOLID) : ((gflags) & ~G1FLAG_SOLID)) #define G1FLAGS_NDIMS(gflags) (2 + G1FLAGS_GET_Z(gflags) + G1FLAGS_GET_M(gflags)) #define G1FLAGS_GET_ZM(gflags) (G1FLAGS_GET_M(gflags) + G1FLAGS_GET_Z(gflags) * 2) #define G1FLAGS_NDIMS_BOX(gflags) (G1FLAGS_GET_GEODETIC(gflags) ? 3 : G1FLAGS_NDIMS(gflags)) uint8_t g1flags(int has_z, int has_m, int is_geodetic); uint8_t lwflags_get_g1flags(lwflags_t lwflags); /* * GSERIALIZED PUBLIC API */ /** * Read the flags from a #GSERIALIZED and return a standard lwflag * integer */ lwflags_t gserialized1_get_lwflags(const GSERIALIZED *g); /** * Copy a new bounding box into an existing gserialized. * If necessary a new #GSERIALIZED will be allocated. Test * that input != output before freeing input. */ GSERIALIZED *gserialized1_set_gbox(GSERIALIZED *g, GBOX *gbox); /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED* gserialized1_drop_gbox(GSERIALIZED *g); /** * Read the box from the #GSERIALIZED or calculate it if necessary. * Return #LWFAILURE if box cannot be calculated (NULL or EMPTY * input). */ int gserialized1_get_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Read the box from the #GSERIALIZED or return #LWFAILURE if * box is unavailable. */ int gserialized1_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ uint32_t gserialized1_get_type(const GSERIALIZED *g); /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ uint32_t gserialized1_max_header_size(void); /** * Returns a hash code for the srid/type/geometry information * in the GSERIALIZED. Ignores metadata like flags and optional * boxes, etc. */ int32_t gserialized1_hash(const GSERIALIZED *g); /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ int32_t gserialized1_get_srid(const GSERIALIZED *g); /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ void gserialized1_set_srid(GSERIALIZED *g, int32_t srid); /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ int gserialized1_is_empty(const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ int gserialized1_has_bbox(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has a Z ordinate. */ int gserialized1_has_z(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an M ordinate. */ int gserialized1_has_m(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED is a geography. */ int gserialized1_is_geodetic(const GSERIALIZED *gser); /** * Return the number of dimensions (2, 3, 4) in a geometry */ int gserialized1_ndims(const GSERIALIZED *gser); /** * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ GSERIALIZED* gserialized1_from_lwgeom(LWGEOM *geom, size_t *size); /** * Return the memory size a GSERIALIZED will occupy for a given LWGEOM. */ size_t gserialized1_from_lwgeom_size(const LWGEOM *geom); /** * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates * that are double aligned and suitable for direct reading using getPoint2d_p_ro */ LWGEOM* lwgeom_from_gserialized1(const GSERIALIZED *g); /** * Point into the float box area of the serialization */ const float * gserialized1_get_float_box_p(const GSERIALIZED *g, size_t *ndims); int gserialized1_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox); int gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point); lwgeom/src/liblwgeom/lwgeom_topo.c0000644000176200001440000060573414314566310017035 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2015-2017 Sandro Santilli * **********************************************************************/ #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 1*/ #include "lwgeom_log.h" #include "liblwgeom_internal.h" #include "liblwgeom_topo_internal.h" #include "lwgeom_geos.h" #include #include /* for PRId64 */ #include #ifdef WIN32 # define LWTFMT_ELEMID "lld" #else # define LWTFMT_ELEMID PRId64 #endif /********************************************************************* * * Backend iface * ********************************************************************/ LWT_BE_IFACE* lwt_CreateBackendIface(const LWT_BE_DATA *data) { LWT_BE_IFACE *iface = lwalloc(sizeof(LWT_BE_IFACE)); iface->data = data; iface->cb = NULL; return iface; } void lwt_BackendIfaceRegisterCallbacks(LWT_BE_IFACE *iface, const LWT_BE_CALLBACKS* cb) { iface->cb = cb; } void lwt_FreeBackendIface(LWT_BE_IFACE* iface) { lwfree(iface); } /********************************************************************* * * Backend wrappers * ********************************************************************/ #define CHECKCB(be, method) do { \ if ( ! (be)->cb || ! (be)->cb->method ) \ lwerror("Callback " # method " not registered by backend"); \ } while (0) #define CB0(be, method) \ CHECKCB(be, method);\ return (be)->cb->method((be)->data) #define CB1(be, method, a1) \ CHECKCB(be, method);\ return (be)->cb->method((be)->data, a1) #define CBT0(to, method) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo) #define CBT1(to, method, a1) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1) #define CBT2(to, method, a1, a2) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2) #define CBT3(to, method, a1, a2, a3) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3) #define CBT4(to, method, a1, a2, a3, a4) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4) #define CBT5(to, method, a1, a2, a3, a4, a5) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5) #define CBT6(to, method, a1, a2, a3, a4, a5, a6) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5, a6) const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE* be) { CB0(be, lastErrorMessage); } LWT_BE_TOPOLOGY * lwt_be_loadTopologyByName(LWT_BE_IFACE *be, const char *name) { CB1(be, loadTopologyByName, name); } static int lwt_be_topoGetSRID(LWT_TOPOLOGY *topo) { CBT0(topo, topoGetSRID); } static double lwt_be_topoGetPrecision(LWT_TOPOLOGY *topo) { CBT0(topo, topoGetPrecision); } static int lwt_be_topoHasZ(LWT_TOPOLOGY *topo) { CBT0(topo, topoHasZ); } int lwt_be_freeTopology(LWT_TOPOLOGY *topo) { CBT0(topo, freeTopology); } LWT_ISO_NODE * lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields) { CBT3(topo, getNodeById, ids, numelems, fields); } LWT_ISO_NODE * lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit) { CBT5(topo, getNodeWithinDistance2D, pt, dist, numelems, fields, limit); } static LWT_ISO_NODE * lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit) { CBT4(topo, getNodeWithinBox2D, box, numelems, fields, limit); } static LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit) { CBT4(topo, getEdgeWithinBox2D, box, numelems, fields, limit); } static LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit) { CBT4(topo, getFaceWithinBox2D, box, numelems, fields, limit); } int lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems) { CBT2(topo, insertNodes, node, numelems); } static int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems) { CBT2(topo, insertFaces, face, numelems); } static int lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems) { CBT2(topo, deleteFacesById, ids, numelems); } static int lwt_be_deleteNodesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems) { CBT2(topo, deleteNodesById, ids, numelems); } LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY* topo) { CBT0(topo, getNextEdgeId); } LWT_ISO_EDGE * lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields) { CBT3(topo, getEdgeById, ids, numelems, fields); } static LWT_ISO_FACE * lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields) { CBT3(topo, getFaceById, ids, numelems, fields); } static LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields) { CBT3(topo, getEdgeByNode, ids, numelems, fields); } static LWT_ISO_EDGE * lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box) { CBT4(topo, getEdgeByFace, ids, numelems, fields, box); } static LWT_ISO_NODE * lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box) { CBT4(topo, getNodeByFace, ids, numelems, fields, box); } LWT_ISO_EDGE * lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit) { CBT5(topo, getEdgeWithinDistance2D, pt, dist, numelems, fields, limit); } int lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems) { CBT2(topo, insertEdges, edge, numelems); } int lwt_be_updateEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields, const LWT_ISO_EDGE* upd_edge, int upd_fields, const LWT_ISO_EDGE* exc_edge, int exc_fields ) { CBT6(topo, updateEdges, sel_edge, sel_fields, upd_edge, upd_fields, exc_edge, exc_fields); } static int lwt_be_updateNodes(LWT_TOPOLOGY* topo, const LWT_ISO_NODE* sel_node, int sel_fields, const LWT_ISO_NODE* upd_node, int upd_fields, const LWT_ISO_NODE* exc_node, int exc_fields ) { CBT6(topo, updateNodes, sel_node, sel_fields, upd_node, upd_fields, exc_node, exc_fields); } static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY* topo, const LWT_ISO_FACE* faces, uint64_t numfaces ) { CBT2(topo, updateFacesById, faces, numfaces); } static int lwt_be_updateEdgesById(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* edges, int numedges, int upd_fields ) { CBT3(topo, updateEdgesById, edges, numedges, upd_fields); } static int lwt_be_updateNodesById(LWT_TOPOLOGY* topo, const LWT_ISO_NODE* nodes, int numnodes, int upd_fields ) { CBT3(topo, updateNodesById, nodes, numnodes, upd_fields); } int lwt_be_deleteEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields ) { CBT2(topo, deleteEdges, sel_edge, sel_fields); } LWT_ELEMID lwt_be_getFaceContainingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt) { CBT1(topo, getFaceContainingPoint, pt); } int lwt_be_updateTopoGeomEdgeSplit(LWT_TOPOLOGY* topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2) { CBT3(topo, updateTopoGeomEdgeSplit, split_edge, new_edge1, new_edge2); } static int lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY* topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2) { CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2); } static int lwt_be_checkTopoGeomRemEdge(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, LWT_ELEMID face_left, LWT_ELEMID face_right) { CBT3(topo, checkTopoGeomRemEdge, edge_id, face_left, face_right); } static int lwt_be_checkTopoGeomRemNode(LWT_TOPOLOGY* topo, LWT_ELEMID node_id, LWT_ELEMID eid1, LWT_ELEMID eid2) { CBT3(topo, checkTopoGeomRemNode, node_id, eid1, eid2); } static int lwt_be_updateTopoGeomFaceHeal(LWT_TOPOLOGY* topo, LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface) { CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface); } static int lwt_be_updateTopoGeomEdgeHeal(LWT_TOPOLOGY* topo, LWT_ELEMID edge1, LWT_ELEMID edge2, LWT_ELEMID newedge) { CBT3(topo, updateTopoGeomEdgeHeal, edge1, edge2, newedge); } static LWT_ELEMID * lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit) { CBT3(topo, getRingEdges, edge, numedges, limit); } /* wrappers of backend wrappers... */ int lwt_be_ExistsCoincidentNode(LWT_TOPOLOGY* topo, LWPOINT* pt) { uint64_t exists = 0; lwt_be_getNodeWithinDistance2D(topo, pt, 0, &exists, 0, -1); if (exists == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return 0; } return exists; } int lwt_be_ExistsEdgeIntersectingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt) { uint64_t exists = 0; lwt_be_getEdgeWithinDistance2D(topo, pt, 0, &exists, 0, -1); if (exists == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return 0; } return exists; } /************************************************************************ * * Utility functions * ************************************************************************/ static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol) { LWGEOM *tmp = src; LWGEOM *tmp2; int changed; int iterations = 0; int maxiterations = lwgeom_count_vertices(tgt); /* GEOS snapping can be unstable */ /* See https://trac.osgeo.org/geos/ticket/760 */ do { tmp2 = lwgeom_snap(tmp, tgt, tol); ++iterations; changed = ( lwgeom_count_vertices(tmp2) != lwgeom_count_vertices(tmp) ); LWDEBUGF(2, "After iteration %d, geometry changed ? %d (%d vs %d vertices)", iterations, changed, lwgeom_count_vertices(tmp2), lwgeom_count_vertices(tmp)); if ( tmp != src ) lwgeom_free(tmp); tmp = tmp2; } while ( changed && iterations <= maxiterations ); LWDEBUGF(1, "It took %d/%d iterations to properly snap", iterations, maxiterations); return tmp; } static void _lwt_release_faces(LWT_ISO_FACE *faces, int num_faces) { int i; for ( i=0; ibe_iface = iface; topo->be_topo = be_topo; topo->srid = lwt_be_topoGetSRID(topo); topo->hasZ = lwt_be_topoHasZ(topo); topo->precision = lwt_be_topoGetPrecision(topo); return topo; } void lwt_FreeTopology( LWT_TOPOLOGY* topo ) { if ( ! lwt_be_freeTopology(topo) ) { lwnotice("Could not release backend topology memory: %s", lwt_be_lastErrorMessage(topo->be_iface)); } lwfree(topo); } /** * @param checkFace if non zero will check the given face * for really containing the point or determine the * face when given face is -1. Use 0 to simply use * the given face value, no matter what (effectively * allowing adding a non-isolated point when used * with face=-1). */ static LWT_ELEMID _lwt_AddIsoNode( LWT_TOPOLOGY* topo, LWT_ELEMID face, LWPOINT* pt, int skipISOChecks, int checkFace ) { LWT_ELEMID foundInFace = -1; if ( lwpoint_is_empty(pt) ) { lwerror("Cannot add empty point as isolated node"); return -1; } if ( ! skipISOChecks ) { if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/ { lwerror("SQL/MM Spatial exception - coincident node"); return -1; } if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) ) /*x*/ { lwerror("SQL/MM Spatial exception - edge crosses node."); return -1; } } if ( checkFace && ( face == -1 || ! skipISOChecks ) ) { foundInFace = lwt_be_getFaceContainingPoint(topo, pt); /*x*/ if ( foundInFace == -2 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( foundInFace == -1 ) foundInFace = 0; } if ( face == -1 ) { face = foundInFace; } else if ( ! skipISOChecks && foundInFace != face ) { #if 0 lwerror("SQL/MM Spatial exception - within face %d (not %d)", foundInFace, face); #else lwerror("SQL/MM Spatial exception - not within face"); #endif return -1; } LWT_ISO_NODE node; node.node_id = -1; node.containing_face = face; node.geom = pt; if ( ! lwt_be_insertNodes(topo, &node, 1) ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } return node.node_id; } LWT_ELEMID lwt_AddIsoNode( LWT_TOPOLOGY* topo, LWT_ELEMID face, LWPOINT* pt, int skipISOChecks ) { return _lwt_AddIsoNode( topo, face, pt, skipISOChecks, 1 ); } /* Check that an edge does not cross an existing node or edge * * @param myself the id of an edge to skip, if any * (for ChangeEdgeGeom). Can use 0 for none. * * Return -1 on cross or error, 0 if everything is fine. * Note that before returning -1, lwerror is invoked... */ static int _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, const LWLINE *geom, LWT_ELEMID myself ) { uint64_t i, num_nodes, num_edges; LWT_ISO_EDGE *edges; LWT_ISO_NODE *nodes; const GBOX *edgebox; GEOSGeometry *edgegg; initGEOS(lwnotice, lwgeom_geos_error); edgegg = LWGEOM2GEOS(lwline_as_lwgeom(geom), 0); if (!edgegg) { lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return -1; } edgebox = lwgeom_get_bbox( lwline_as_lwgeom(geom) ); /* loop over each node within the edge's gbox */ nodes = lwt_be_getNodeWithinBox2D( topo, edgebox, &num_nodes, LWT_COL_NODE_ALL, 0 ); LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", num_nodes); if (num_nodes == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; inode_id == start_node ) continue; if ( node->node_id == end_node ) continue; /* check if the edge contains this node (not on boundary) */ /* ST_RelateMatch(rec.relate, 'T********') */ pt = getPoint2d_cp(node->geom->point, 0); int contains = ptarray_contains_point_partial(geom->points, pt, LW_FALSE, NULL) == LW_BOUNDARY; if ( contains ) { GEOSGeom_destroy(edgegg); _lwt_release_nodes(nodes, num_nodes); lwerror("SQL/MM Spatial exception - geometry crosses a node"); return -1; } } if ( nodes ) _lwt_release_nodes(nodes, num_nodes); /* may be NULL if num_nodes == 0 */ /* loop over each edge within the edge's gbox */ edges = lwt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, LWT_COL_EDGE_ALL, 0 ); LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %d edges", num_edges); if (num_edges == UINT64_MAX) { GEOSGeom_destroy(edgegg); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; iedge_id; GEOSGeometry *eegg; char *relate; int match; if ( edge_id == myself ) continue; if ( ! edge->geom ) { _lwt_release_edges(edges, num_edges); lwerror("Edge %d has NULL geometry!", edge_id); return -1; } eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 ); if ( ! eegg ) { GEOSGeom_destroy(edgegg); _lwt_release_edges(edges, num_edges); lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return -1; } LWDEBUGF(2, "Edge %d converted to GEOS", edge_id); /* check if the edge crosses our edge (not boundary-boundary) */ relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2); if ( ! relate ) { GEOSGeom_destroy(eegg); GEOSGeom_destroy(edgegg); _lwt_release_edges(edges, num_edges); lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg); return -1; } LWDEBUGF(2, "Edge %d relate pattern is %s", edge_id, relate); match = GEOSRelatePatternMatch(relate, "F********"); if ( match ) { /* error or no interior intersection */ GEOSGeom_destroy(eegg); GEOSFree(relate); if ( match == 2 ) { _lwt_release_edges(edges, num_edges); GEOSGeom_destroy(edgegg); lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); return -1; } else continue; /* no interior intersection */ } match = GEOSRelatePatternMatch(relate, "1FFF*FFF2"); if ( match ) { _lwt_release_edges(edges, num_edges); GEOSGeom_destroy(edgegg); GEOSGeom_destroy(eegg); GEOSFree(relate); if ( match == 2 ) { lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); } else { lwerror("SQL/MM Spatial exception - coincident edge %" LWTFMT_ELEMID, edge_id); } return -1; } match = GEOSRelatePatternMatch(relate, "1********"); if ( match ) { _lwt_release_edges(edges, num_edges); GEOSGeom_destroy(edgegg); GEOSGeom_destroy(eegg); GEOSFree(relate); if ( match == 2 ) { lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); } else { lwerror("Spatial exception - geometry intersects edge %" LWTFMT_ELEMID, edge_id); } return -1; } match = GEOSRelatePatternMatch(relate, "T********"); if ( match ) { _lwt_release_edges(edges, num_edges); GEOSGeom_destroy(edgegg); GEOSGeom_destroy(eegg); GEOSFree(relate); if ( match == 2 ) { lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); } else { lwerror("SQL/MM Spatial exception - geometry crosses edge %" LWTFMT_ELEMID, edge_id); } return -1; } LWDEBUGF(2, "Edge %d analisys completed, it does no harm", edge_id); GEOSFree(relate); GEOSGeom_destroy(eegg); } if ( edges ) _lwt_release_edges(edges, num_edges); /* would be NULL if num_edges was 0 */ GEOSGeom_destroy(edgegg); return 0; } LWT_ELEMID lwt_AddIsoEdge( LWT_TOPOLOGY* topo, LWT_ELEMID startNode, LWT_ELEMID endNode, const LWLINE* geom ) { uint64_t num_nodes; uint64_t i; LWT_ISO_EDGE newedge; LWT_ISO_NODE *endpoints; LWT_ELEMID containing_face = -1; LWT_ELEMID node_ids[2]; LWT_ISO_NODE updated_nodes[2]; int skipISOChecks = 0; POINT2D p1, p2; /* NOT IN THE SPECS: * A closed edge is never isolated (as it forms a face) */ if (startNode == endNode) { lwerror("Closed edges would not be isolated, try lwt_AddEdgeNewFaces"); return -1; } if ( ! skipISOChecks ) { /* Acurve must be simple */ if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) ) { lwerror("SQL/MM Spatial exception - curve not simple"); return -1; } } /* * Check for: * existence of nodes * nodes faces match * Extract: * nodes face id * nodes geoms */ num_nodes = 2; node_ids[0] = startNode; node_ids[1] = endNode; endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL ); if (num_nodes == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( num_nodes < 2 ) { if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - non-existent node"); return -1; } for ( i=0; icontaining_face == -1 ) { _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - not isolated node"); return -1; } if ( containing_face == -1 ) containing_face = n->containing_face; else if ( containing_face != n->containing_face ) { _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - nodes in different faces"); return -1; } if ( ! skipISOChecks ) { if ( n->node_id == startNode ) { /* l) Check that start point of acurve match start node geoms. */ getPoint2d_p(geom->points, 0, &p1); getPoint2d_p(n->geom->point, 0, &p2); if ( ! p2d_same(&p1, &p2) ) { _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - " "start node not geometry start point."); return -1; } } else { /* m) Check that end point of acurve match end node geoms. */ getPoint2d_p(geom->points, geom->points->npoints-1, &p1); getPoint2d_p(n->geom->point, 0, &p2); if ( ! p2d_same(&p1, &p2) ) { _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - " "end node not geometry end point."); return -1; } } } } if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); if ( ! skipISOChecks ) { if ( _lwt_CheckEdgeCrossing( topo, startNode, endNode, geom, 0 ) ) { /* would have called lwerror already, leaking :( */ return -1; } } /* * All checks passed, time to prepare the new edge */ newedge.edge_id = lwt_be_getNextEdgeId( topo ); if ( newedge.edge_id == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* TODO: this should likely be an exception instead ! */ if ( containing_face == -1 ) containing_face = 0; newedge.start_node = startNode; newedge.end_node = endNode; newedge.face_left = newedge.face_right = containing_face; newedge.next_left = -newedge.edge_id; newedge.next_right = newedge.edge_id; newedge.geom = (LWLINE *)geom; /* const cast.. */ int ret = lwt_be_insertEdges(topo, &newedge, 1); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { lwerror("Insertion of split edge failed (no reason)"); return -1; } /* * Update Node containing_face values * * the nodes anode and anothernode are no more isolated * because now there is an edge connecting them */ updated_nodes[0].node_id = startNode; updated_nodes[0].containing_face = -1; updated_nodes[1].node_id = endNode; updated_nodes[1].containing_face = -1; ret = lwt_be_updateNodesById(topo, updated_nodes, 2, LWT_COL_NODE_CONTAINING_FACE); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } return newedge.edge_id; } static LWCOLLECTION * _lwt_EdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks, LWT_ISO_EDGE** oldedge ) { LWGEOM *split; LWCOLLECTION *split_col; uint64_t i; /* Get edge */ i = 1; LWDEBUG(1, "calling lwt_be_getEdgeById"); *oldedge = lwt_be_getEdgeById(topo, &edge, &i, LWT_COL_EDGE_ALL); LWDEBUGF(1, "lwt_be_getEdgeById returned %p", *oldedge); if ( ! *oldedge ) { LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i); if (i == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } else if ( i == 0 ) { lwerror("SQL/MM Spatial exception - non-existent edge"); return NULL; } else { lwerror("Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return NULL; } } /* * - check if a coincident node already exists */ if ( ! skipISOChecks ) { LWDEBUG(1, "calling lwt_be_ExistsCoincidentNode"); if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/ { LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned"); _lwt_release_edges(*oldedge, 1); lwerror("SQL/MM Spatial exception - coincident node"); return NULL; } LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned"); } /* Split edge */ split = lwgeom_split((LWGEOM*)(*oldedge)->geom, (LWGEOM*)pt); if ( ! split ) { _lwt_release_edges(*oldedge, 1); lwerror("could not split edge by point ?"); return NULL; } split_col = lwgeom_as_lwcollection(split); if ( ! split_col ) { _lwt_release_edges(*oldedge, 1); lwgeom_free(split); lwerror("lwgeom_as_lwcollection returned NULL"); return NULL; } if (split_col->ngeoms < 2) { _lwt_release_edges(*oldedge, 1); lwgeom_free(split); lwerror("SQL/MM Spatial exception - point not on edge"); return NULL; } #if 0 { size_t sz; char *wkt = lwgeom_to_wkt((LWGEOM*)split_col, WKT_EXTENDED, 2, &sz); LWDEBUGF(1, "returning split col: %s", wkt); lwfree(wkt); } #endif return split_col; } LWT_ELEMID lwt_ModEdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks ) { LWT_ISO_NODE node; LWT_ISO_EDGE* oldedge = NULL; LWCOLLECTION *split_col; const LWGEOM *oldedge_geom; const LWGEOM *newedge_geom; LWT_ISO_EDGE newedge1; LWT_ISO_EDGE seledge, updedge, excedge; int ret; split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge ); if ( ! split_col ) return -1; /* should have raised an exception */ oldedge_geom = split_col->geoms[0]; newedge_geom = split_col->geoms[1]; /* Make sure the SRID is set on the subgeom */ ((LWGEOM*)oldedge_geom)->srid = split_col->srid; ((LWGEOM*)newedge_geom)->srid = split_col->srid; /* Add new node, getting new id back */ node.node_id = -1; node.containing_face = -1; /* means not-isolated */ node.geom = pt; if ( ! lwt_be_insertNodes(topo, &node, 1) ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if (node.node_id == -1) { /* should have been set by backend */ _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend coding error: " "insertNodes callback did not return node_id"); return -1; } /* Insert the new edge */ newedge1.edge_id = lwt_be_getNextEdgeId(topo); if ( newedge1.edge_id == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } newedge1.start_node = node.node_id; newedge1.end_node = oldedge->end_node; newedge1.face_left = oldedge->face_left; newedge1.face_right = oldedge->face_right; newedge1.next_left = oldedge->next_left == -oldedge->edge_id ? -newedge1.edge_id : oldedge->next_left; newedge1.next_right = -oldedge->edge_id; newedge1.geom = lwgeom_as_lwline(newedge_geom); /* lwgeom_split of a line should only return lines ... */ if ( ! newedge1.geom ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("first geometry in lwgeom_split output is not a line"); return -1; } ret = lwt_be_insertEdges(topo, &newedge1, 1); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Insertion of split edge failed (no reason)"); return -1; } /* Update the old edge */ updedge.geom = lwgeom_as_lwline(oldedge_geom); /* lwgeom_split of a line should only return lines ... */ if ( ! updedge.geom ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("second geometry in lwgeom_split output is not a line"); return -1; } updedge.next_left = newedge1.edge_id; updedge.end_node = node.node_id; ret = lwt_be_updateEdges(topo, oldedge, LWT_COL_EDGE_EDGE_ID, &updedge, LWT_COL_EDGE_GEOM|LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE, NULL, 0); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Edge being split (%d) disappeared during operations?", oldedge->edge_id); return -1; } else if ( ret > 1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("More than a single edge found with id %d !", oldedge->edge_id); return -1; } /* Update all next edge references to match new layout (ST_ModEdgeSplit) */ updedge.next_right = -newedge1.edge_id; excedge.edge_id = newedge1.edge_id; seledge.next_right = -oldedge->edge_id; seledge.start_node = oldedge->end_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT|LWT_COL_EDGE_START_NODE, &updedge, LWT_COL_EDGE_NEXT_RIGHT, &excedge, LWT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = -newedge1.edge_id; excedge.edge_id = newedge1.edge_id; seledge.next_left = -oldedge->edge_id; seledge.end_node = oldedge->end_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE, &updedge, LWT_COL_EDGE_NEXT_LEFT, &excedge, LWT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Update TopoGeometries composition */ ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedge1.edge_id, -1); if ( ! ret ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); /* return new node id */ return node.node_id; } LWT_ELEMID lwt_NewEdgesSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks ) { LWT_ISO_NODE node; LWT_ISO_EDGE* oldedge = NULL; LWCOLLECTION *split_col; const LWGEOM *oldedge_geom; const LWGEOM *newedge_geom; LWT_ISO_EDGE newedges[2]; LWT_ISO_EDGE seledge, updedge; int ret; split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge ); if ( ! split_col ) return -1; /* should have raised an exception */ oldedge_geom = split_col->geoms[0]; newedge_geom = split_col->geoms[1]; /* Make sure the SRID is set on the subgeom */ ((LWGEOM*)oldedge_geom)->srid = split_col->srid; ((LWGEOM*)newedge_geom)->srid = split_col->srid; /* Add new node, getting new id back */ node.node_id = -1; node.containing_face = -1; /* means not-isolated */ node.geom = pt; if ( ! lwt_be_insertNodes(topo, &node, 1) ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if (node.node_id == -1) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); /* should have been set by backend */ lwerror("Backend coding error: " "insertNodes callback did not return node_id"); return -1; } /* Delete the old edge */ seledge.edge_id = edge; ret = lwt_be_deleteEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Get new edges identifiers */ newedges[0].edge_id = lwt_be_getNextEdgeId(topo); if ( newedges[0].edge_id == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } newedges[1].edge_id = lwt_be_getNextEdgeId(topo); if ( newedges[1].edge_id == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Define the first new edge (to new node) */ newedges[0].start_node = oldedge->start_node; newedges[0].end_node = node.node_id; newedges[0].face_left = oldedge->face_left; newedges[0].face_right = oldedge->face_right; newedges[0].next_left = newedges[1].edge_id; if ( oldedge->next_right == edge ) newedges[0].next_right = newedges[0].edge_id; else if ( oldedge->next_right == -edge ) newedges[0].next_right = -newedges[1].edge_id; else newedges[0].next_right = oldedge->next_right; newedges[0].geom = lwgeom_as_lwline(oldedge_geom); /* lwgeom_split of a line should only return lines ... */ if ( ! newedges[0].geom ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("first geometry in lwgeom_split output is not a line"); return -1; } /* Define the second new edge (from new node) */ newedges[1].start_node = node.node_id; newedges[1].end_node = oldedge->end_node; newedges[1].face_left = oldedge->face_left; newedges[1].face_right = oldedge->face_right; newedges[1].next_right = -newedges[0].edge_id; if ( oldedge->next_left == -edge ) newedges[1].next_left = -newedges[1].edge_id; else if ( oldedge->next_left == edge ) newedges[1].next_left = newedges[0].edge_id; else newedges[1].next_left = oldedge->next_left; newedges[1].geom = lwgeom_as_lwline(newedge_geom); /* lwgeom_split of a line should only return lines ... */ if ( ! newedges[1].geom ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("second geometry in lwgeom_split output is not a line"); return -1; } /* Insert both new edges */ ret = lwt_be_insertEdges(topo, newedges, 2); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Insertion of split edge failed (no reason)"); return -1; } /* Update all next edge references pointing to old edge id */ updedge.next_right = newedges[1].edge_id; seledge.next_right = edge; seledge.start_node = oldedge->start_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT|LWT_COL_EDGE_START_NODE, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_right = -newedges[0].edge_id; seledge.next_right = -edge; seledge.start_node = oldedge->end_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT|LWT_COL_EDGE_START_NODE, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = newedges[0].edge_id; seledge.next_left = edge; seledge.end_node = oldedge->start_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = -newedges[1].edge_id; seledge.next_left = -edge; seledge.end_node = oldedge->end_node; ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( ret == -1 ) { _lwt_release_edges(oldedge, 1); lwcollection_release(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Update TopoGeometries composition */ ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedges[0].edge_id, newedges[1].edge_id); if ( ! ret ) { _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } _lwt_release_edges(oldedge, 1); lwcollection_free(split_col); /* return new node id */ return node.node_id; } /* Data structure used by AddEdgeX functions */ typedef struct edgeend_t { /* Signed identifier of next clockwise edge (+outgoing,-incoming) */ LWT_ELEMID nextCW; /* Identifier of face between myaz and next CW edge */ LWT_ELEMID cwFace; /* Signed identifier of next counterclockwise edge (+outgoing,-incoming) */ LWT_ELEMID nextCCW; /* Identifier of face between myaz and next CCW edge */ LWT_ELEMID ccwFace; int was_isolated; double myaz; /* azimuth of edgeend geometry */ } edgeend; /* * Get first distinct vertex from endpoint * @param pa the pointarray to seek points in * @param ref the point we want to search a distinct one * @param from vertex index to start from (will really start from "from"+dir) * @param dir 1 to go forward * -1 to go backward * @return 0 if edge is collapsed (no distinct points) */ static int _lwt_FirstDistinctVertex2D(const POINTARRAY* pa, POINT2D *ref, int from, int dir, POINT2D *op) { int i, toofar, inc; POINT2D fp; if ( dir > 0 ) { toofar = pa->npoints; inc = 1; } else { toofar = -1; inc = -1; } LWDEBUGF(1, "first point is index %d", from); fp = *ref; /* getPoint2d_p(pa, from, &fp); */ for ( i = from+inc; i != toofar; i += inc ) { LWDEBUGF(1, "testing point %d", i); getPoint2d_p(pa, i, op); /* pick next point */ if ( p2d_same(op, &fp) ) continue; /* equal to startpoint */ /* this is a good one, neither same of start nor of end point */ return 1; /* found */ } /* no distinct vertices found */ return 0; } /* * Return non-zero on failure (lwerror is invoked in that case) * Possible failures: * -1 no two distinct vertices exist * -2 azimuth computation failed for first edge end */ static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp) { POINTARRAY *pa = edge->points; POINT2D pt; fee->nextCW = fee->nextCCW = lee->nextCW = lee->nextCCW = 0; fee->cwFace = fee->ccwFace = lee->cwFace = lee->ccwFace = -1; /* Compute azimuth of first edge end */ LWDEBUG(1, "computing azimuth of first edge end"); if ( ! _lwt_FirstDistinctVertex2D(pa, fp, 0, 1, &pt) ) { lwerror("Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(fp, &pt, &(fee->myaz)) ) { lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]", fp->x, fp->y, pt.x, pt.y); return -2; } LWDEBUGF(1, "azimuth of first edge end [%.15g %.15g,%.15g %.15g] is %g", fp->x, fp->y, pt.x, pt.y, fee->myaz); /* Compute azimuth of second edge end */ LWDEBUG(1, "computing azimuth of second edge end"); if ( ! _lwt_FirstDistinctVertex2D(pa, lp, pa->npoints-1, -1, &pt) ) { lwerror("Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(lp, &pt, &(lee->myaz)) ) { lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]", lp->x, lp->y, pt.x, pt.y); return -2; } LWDEBUGF(1, "azimuth of last edge end [%.15g %.15g,%.15g %.15g] is %g", lp->x, lp->y, pt.x, pt.y, lee->myaz); return 0; } /* * Find the first edges encountered going clockwise and counterclockwise * around a node, starting from the given azimuth, and take * note of the face on the both sides. * * @param topo the topology to act upon * @param node the identifier of the node to analyze * @param data input (myaz) / output (nextCW, nextCCW) parameter * @param other edgeend, if also incident to given node (closed edge). * @param myedge_id identifier of the edge id that data->myaz belongs to * @return number of incident edges found * */ static int _lwt_FindAdjacentEdges( LWT_TOPOLOGY* topo, LWT_ELEMID node, edgeend *data, edgeend *other, int myedge_id ) { LWT_ISO_EDGE *edges; uint64_t numedges = 1; uint64_t i; double minaz, maxaz; double az, azdif; data->nextCW = data->nextCCW = 0; data->cwFace = data->ccwFace = -1; if ( other ) { azdif = other->myaz - data->myaz; if ( azdif < 0 ) azdif += 2 * M_PI; minaz = maxaz = azdif; /* TODO: set nextCW/nextCCW/cwFace/ccwFace to other->something ? */ LWDEBUGF(1, "Other edge end has cwFace=%d and ccwFace=%d", other->cwFace, other->ccwFace); } else { minaz = maxaz = -1; } LWDEBUGF(1, "Looking for edges incident to node %" LWTFMT_ELEMID " and adjacent to azimuth %g", node, data->myaz); /* Get incident edges */ edges = lwt_be_getEdgeByNode( topo, &node, &numedges, LWT_COL_EDGE_ALL ); if (numedges == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return 0; } LWDEBUGF(1, "getEdgeByNode returned %d edges, minaz=%g, maxaz=%g", numedges, minaz, maxaz); /* For each incident edge-end (1 or 2): */ for ( i = 0; i < numedges; ++i ) { LWT_ISO_EDGE *edge; LWGEOM *g; LWGEOM *cleangeom; POINT2D p1, p2; POINTARRAY *pa; edge = &(edges[i]); if ( edge->edge_id == myedge_id ) continue; g = lwline_as_lwgeom(edge->geom); /* NOTE: remove_repeated_points call could be replaced by * some other mean to pick two distinct points for endpoints */ cleangeom = lwgeom_remove_repeated_points( g, 0 ); pa = lwgeom_as_lwline(cleangeom)->points; if ( pa->npoints < 2 ) {{ LWT_ELEMID id = edge->edge_id; _lwt_release_edges(edges, numedges); lwgeom_free(cleangeom); lwerror("corrupted topology: edge %" LWTFMT_ELEMID " does not have two distinct points", id); return -1; }} if ( edge->start_node == node ) { getPoint2d_p(pa, 0, &p1); if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &p2) ) { lwerror("Edge %d has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ", edge->edge_id, p1.x, p1.y, p2.x, p2.y); return -1; } LWDEBUGF(1, "edge %" LWTFMT_ELEMID " starts on node %" LWTFMT_ELEMID ", edgeend is %g,%g-%g,%g", edge->edge_id, node, p1.x, p1.y, p2.x, p2.y); if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{ LWT_ELEMID id = edge->edge_id; _lwt_release_edges(edges, numedges); lwgeom_free(cleangeom); lwerror("error computing azimuth of edge %d first edgeend [%.15g %.15g,%.15g %.15g]", id, p1.x, p1.y, p2.x, p2.y); return -1; }} azdif = az - data->myaz; LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID ": %g (diff: %g)", edge->edge_id, az, azdif); if ( azdif < 0 ) azdif += 2 * M_PI; if ( minaz == -1 ) { minaz = maxaz = azdif; data->nextCW = data->nextCCW = edge->edge_id; /* outgoing */ data->cwFace = edge->face_left; data->ccwFace = edge->face_right; LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID ", outgoing, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (face_right is new ccwFace, face_left is new cwFace)", edge->edge_id, edge->face_left, edge->face_right); } else { if ( azdif < minaz ) { data->nextCW = edge->edge_id; /* outgoing */ data->cwFace = edge->face_left; LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID ", outgoing, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (previous had minaz=%g, face_left is new cwFace)", edge->edge_id, edge->face_left, edge->face_right, minaz); minaz = azdif; } else if ( azdif > maxaz ) { data->nextCCW = edge->edge_id; /* outgoing */ data->ccwFace = edge->face_right; LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID ", outgoing, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (previous had maxaz=%g, face_right is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right, maxaz); maxaz = azdif; } } } if ( edge->end_node == node ) { getPoint2d_p(pa, pa->npoints-1, &p1); if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, pa->npoints-1, -1, &p2) ) { lwerror("Edge %d has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ", edge->edge_id, p1.x, p1.y, p2.x, p2.y); return -1; } LWDEBUGF(1, "edge %" LWTFMT_ELEMID " ends on node %" LWTFMT_ELEMID ", edgeend is %g,%g-%g,%g", edge->edge_id, node, p1.x, p1.y, p2.x, p2.y); if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{ LWT_ELEMID id = edge->edge_id; _lwt_release_edges(edges, numedges); lwgeom_free(cleangeom); lwerror("error computing azimuth of edge %d last edgeend [%.15g %.15g,%.15g %.15g]", id, p1.x, p1.y, p2.x, p2.y); return -1; }} azdif = az - data->myaz; LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID ": %g (diff: %g)", edge->edge_id, az, azdif); if ( azdif < 0 ) azdif += 2 * M_PI; if ( minaz == -1 ) { minaz = maxaz = azdif; data->nextCW = data->nextCCW = -edge->edge_id; /* incoming */ data->cwFace = edge->face_right; data->ccwFace = edge->face_left; LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID ", incoming, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (face_right is new cwFace, face_left is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right); } else { if ( azdif < minaz ) { data->nextCW = -edge->edge_id; /* incoming */ data->cwFace = edge->face_right; LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID ", incoming, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (previous had minaz=%g, face_right is new cwFace)", edge->edge_id, edge->face_left, edge->face_right, minaz); minaz = azdif; } else if ( azdif > maxaz ) { data->nextCCW = -edge->edge_id; /* incoming */ data->ccwFace = edge->face_left; LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID ", outgoing, from start point, " "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID " (previous had maxaz=%g, face_left is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right, maxaz); maxaz = azdif; } } } lwgeom_free(cleangeom); } if ( numedges ) _lwt_release_edges(edges, numedges); LWDEBUGF(1, "edges adjacent to azimuth %g" " (incident to node %" LWTFMT_ELEMID ")" ": CW:%" LWTFMT_ELEMID "(%g) CCW:%" LWTFMT_ELEMID "(%g)", data->myaz, node, data->nextCW, minaz, data->nextCCW, maxaz); if ( myedge_id < 1 && numedges && data->cwFace != data->ccwFace ) { if ( data->cwFace != -1 && data->ccwFace != -1 ) { lwerror("Corrupted topology: adjacent edges %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID " bind different face (%" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")", data->nextCW, data->nextCCW, data->cwFace, data->ccwFace); return -1; } } /* Return number of incident edges found */ return numedges; } /* * Get a point internal to the line and write it into the "ip" * parameter * * return 0 on failure (line is empty or collapsed), 1 otherwise */ static int _lwt_GetInteriorEdgePoint(const LWLINE* edge, POINT2D* ip) { uint32_t i; POINT2D fp, lp, tp; POINTARRAY *pa = edge->points; if ( pa->npoints < 2 ) return 0; /* empty or structurally collapsed */ getPoint2d_p(pa, 0, &fp); /* save first point */ getPoint2d_p(pa, pa->npoints-1, &lp); /* save last point */ for (i=1; inpoints-1; ++i) { getPoint2d_p(pa, i, &tp); /* pick next point */ if ( p2d_same(&tp, &fp) ) continue; /* equal to startpoint */ if ( p2d_same(&tp, &lp) ) continue; /* equal to endpoint */ /* this is a good one, neither same of start nor of end point */ *ip = tp; return 1; /* found */ } /* no distinct vertex found */ /* interpolate if start point != end point */ if ( p2d_same(&fp, &lp) ) return 0; /* no distinct points in edge */ ip->x = fp.x + ( (lp.x - fp.x) * 0.5 ); ip->y = fp.y + ( (lp.y - fp.y) * 0.5 ); return 1; } static LWPOLY * _lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids) { LWT_ELEMID *edge_ids; uint64_t numedges, i, j; LWT_ISO_EDGE *ring_edges; /* Construct a polygon using edges of the ring */ numedges = 0; edge_ids = lwalloc(sizeof(LWT_ELEMID)*num_signed_edge_ids); for (i=0; ibe_iface)); return NULL; } else if ( i != numedges ) { lwfree( signed_edge_ids ); _lwt_release_edges(ring_edges, numedges); lwerror("Unexpected error: %d edges found when expecting %d", i, numedges); return NULL; } /* Should now build a polygon with those edges, in the order * given by GetRingEdges. */ POINTARRAY *pa = NULL; for ( i=0; igeom->points); if ( eid < 0 ) ptarray_reverse_in_place(pa); } else { if ( eid < 0 ) { epa = ptarray_clone_deep(edge->geom->points); ptarray_reverse_in_place(epa); ptarray_append_ptarray(pa, epa, 0); ptarray_free(epa); } else { /* avoid a clone here */ ptarray_append_ptarray(pa, edge->geom->points, 0); } } } _lwt_release_edges(ring_edges, numedges); POINTARRAY **points = lwalloc(sizeof(POINTARRAY*)); points[0] = pa; /* NOTE: the ring may very well have collapsed components, * which would make it topologically invalid */ LWPOLY* shell = lwpoly_construct(0, 0, 1, points); return shell; } /* * Add a split face by walking on the edge side. * * @param topo the topology to act upon * @param sedge edge id and walking side and direction * (forward,left:positive backward,right:negative) * @param face the face in which the edge identifier is known to be * @param mbr_only do not create a new face but update MBR of the current * * @return: * -1: if mbr_only was requested * 0: if the edge does not form a ring * -1: if it is impossible to create a face on the requested side * ( new face on the side is the universe ) * -2: error * >0 : id of newly added face */ static LWT_ELEMID _lwt_AddFaceSplit( LWT_TOPOLOGY* topo, LWT_ELEMID sedge, LWT_ELEMID face, int mbr_only ) { uint64_t numfaceedges, i, j; int newface_outside; uint64_t num_signed_edge_ids; LWT_ELEMID *signed_edge_ids; LWT_ISO_EDGE *edges; LWT_ISO_EDGE *forward_edges = NULL; int forward_edges_count = 0; LWT_ISO_EDGE *backward_edges = NULL; int backward_edges_count = 0; signed_edge_ids = lwt_be_getRingEdges(topo, sedge, &num_signed_edge_ids, 0); if (!signed_edge_ids) { lwerror("Backend error (no ring edges for edge %" LWTFMT_ELEMID "): %s", sedge, lwt_be_lastErrorMessage(topo->be_iface)); return -2; } LWDEBUGF(1, "getRingEdges returned %d edges", num_signed_edge_ids); /* You can't get to the other side of an edge forming a ring */ for (i=0; ibe_iface)); return -2; } const POINTARRAY *pa = shell->rings[0]; int isccw = ptarray_isccw(pa); LWDEBUGF(1, "Ring of edge %" LWTFMT_ELEMID " is %sclockwise", sedge, isccw ? "counter" : ""); const GBOX* shellbox = lwgeom_get_bbox(lwpoly_as_lwgeom(shell)); if ( face == 0 ) { /* Edge split the universe face */ if ( ! isccw ) { lwpoly_free(shell); lwfree( signed_edge_ids ); /* Face on the left side of this ring is the universe face. * Next call (for the other side) should create the split face */ LWDEBUG(1, "The left face of this clockwise ring is the universe, " "won't create a new face there"); return -1; } } if ( mbr_only && face != 0 ) { if ( isccw ) {{ LWT_ISO_FACE updface; updface.face_id = face; updface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */ int ret = lwt_be_updateFacesById( topo, &updface, 1 ); if ( ret == -1 ) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox above */ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != 1 ) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox above */ lwerror("Unexpected error: %d faces found when expecting 1", ret); return -2; } }} lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox above */ return -1; /* mbr only was requested */ } LWT_ISO_FACE *oldface = NULL; LWT_ISO_FACE newface; newface.face_id = -1; if ( face != 0 && ! isccw) {{ /* Face created an hole in an outer face */ uint64_t nfaces = 1; oldface = lwt_be_getFaceById(topo, &face, &nfaces, LWT_COL_FACE_ALL); if (nfaces == UINT64_MAX) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox */ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( nfaces != 1 ) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox */ lwerror("Unexpected error: %d faces found when expecting 1", nfaces); return -2; } newface.mbr = oldface->mbr; }} else { newface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */ } /* Insert the new face */ int ret = lwt_be_insertFaces( topo, &newface, 1 ); if ( ret == -1 ) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox */ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != 1 ) { lwfree( signed_edge_ids ); lwpoly_free(shell); /* NOTE: owns shellbox */ lwerror("Unexpected error: %d faces inserted when expecting 1", ret); return -2; } if ( oldface ) { newface.mbr = NULL; /* it is a reference to oldface mbr... */ _lwt_release_faces(oldface, 1); } /* Update side location of new face edges */ /* We want the new face to be on the left, if possible */ if ( face != 0 && ! isccw ) { /* ring is clockwise in a real face */ /* face shrinked, must update all non-contained edges and nodes */ LWDEBUG(1, "New face is on the outside of the ring, updating rings in former shell"); newface_outside = 1; /* newface is outside */ } else { LWDEBUG(1, "New face is on the inside of the ring, updating forward edges in new ring"); newface_outside = 0; /* newface is inside */ } /* Update edges bounding the old face */ /* (1) fetch all edges where left_face or right_face is = oldface */ int fields = LWT_COL_EDGE_EDGE_ID | LWT_COL_EDGE_FACE_LEFT | LWT_COL_EDGE_FACE_RIGHT | LWT_COL_EDGE_GEOM ; numfaceedges = 1; edges = lwt_be_getEdgeByFace( topo, &face, &numfaceedges, fields, newface.mbr ); if (numfaceedges == UINT64_MAX) { lwfree(signed_edge_ids); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } LWDEBUGF(1, "lwt_be_getEdgeByFace returned %d edges", numfaceedges); if ( numfaceedges ) { forward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges); forward_edges_count = 0; backward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges); backward_edges_count = 0; /* (2) loop over the results and: */ for ( i=0; iedge_id ) { /* IDEA: remove entry from signed_edge_ids ? */ LWDEBUGF(1, "Edge %d is a forward edge of the new ring", e->edge_id); forward_edges[forward_edges_count].edge_id = e->edge_id; forward_edges[forward_edges_count++].face_left = newface.face_id; found++; if ( found == 2 ) break; } else if ( -seid == e->edge_id ) { /* IDEA: remove entry from signed_edge_ids ? */ LWDEBUGF(1, "Edge %d is a backward edge of the new ring", e->edge_id); backward_edges[backward_edges_count].edge_id = e->edge_id; backward_edges[backward_edges_count++].face_right = newface.face_id; found++; if ( found == 2 ) break; } } if ( found ) continue; /* We need to check only a single point * (to avoid collapsed elements of the shell polygon * giving false positive). * The point but must not be an endpoint. */ if ( ! _lwt_GetInteriorEdgePoint(e->geom, &ep) ) { lwfree(signed_edge_ids); lwpoly_free(shell); lwfree(forward_edges); /* contents owned by edges */ lwfree(backward_edges); /* contents owned by edges */ _lwt_release_edges(edges, numfaceedges); lwerror("Could not find interior point for edge %d: %s", e->edge_id, lwgeom_geos_errmsg); return -2; } /* IDEA: check that bounding box shortcut is taken, or use * shellbox to do it here */ contains = ptarray_contains_point(pa, &ep) == LW_INSIDE; if ( contains == 2 ) LWDEBUGF(1, "Edge %d %scontained in new ring", e->edge_id, (contains?"":"not ")); /* (2.2) skip edges (NOT, if newface_outside) contained in shell */ if ( newface_outside ) { if ( contains ) { LWDEBUGF(1, "Edge %d contained in an hole of the new face", e->edge_id); continue; } } else { if ( ! contains ) { LWDEBUGF(1, "Edge %d not contained in the face shell", e->edge_id); continue; } } /* (2.3) push to forward_edges if left_face = oface */ if ( e->face_left == face ) { LWDEBUGF(1, "Edge %d has new face on the left side", e->edge_id); forward_edges[forward_edges_count].edge_id = e->edge_id; forward_edges[forward_edges_count++].face_left = newface.face_id; } /* (2.4) push to backward_edges if right_face = oface */ if ( e->face_right == face ) { LWDEBUGF(1, "Edge %d has new face on the right side", e->edge_id); backward_edges[backward_edges_count].edge_id = e->edge_id; backward_edges[backward_edges_count++].face_right = newface.face_id; } } /* Update forward edges */ if ( forward_edges_count ) { ret = lwt_be_updateEdgesById(topo, forward_edges, forward_edges_count, LWT_COL_EDGE_FACE_LEFT); if ( ret == -1 ) { lwfree( signed_edge_ids ); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != forward_edges_count ) { lwfree( signed_edge_ids ); lwerror("Unexpected error: %d edges updated when expecting %d", ret, forward_edges_count); return -2; } } /* Update backward edges */ if ( backward_edges_count ) { ret = lwt_be_updateEdgesById(topo, backward_edges, backward_edges_count, LWT_COL_EDGE_FACE_RIGHT); if ( ret == -1 ) { lwfree( signed_edge_ids ); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != backward_edges_count ) { lwfree( signed_edge_ids ); lwerror("Unexpected error: %d edges updated when expecting %d", ret, backward_edges_count); return -2; } } lwfree(forward_edges); lwfree(backward_edges); } _lwt_release_edges(edges, numfaceedges); /* Update isolated nodes which are now in new face */ uint64_t numisonodes = 1; fields = LWT_COL_NODE_NODE_ID | LWT_COL_NODE_GEOM; LWT_ISO_NODE *nodes = lwt_be_getNodeByFace(topo, &face, &numisonodes, fields, newface.mbr); if (numisonodes == UINT64_MAX) { lwfree(signed_edge_ids); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( numisonodes ) { LWT_ISO_NODE *updated_nodes = lwalloc(sizeof(LWT_ISO_NODE)*numisonodes); int nodes_to_update = 0; for (i=0; igeom->point, 0); int contains = ptarray_contains_point(pa, pt) == LW_INSIDE; LWDEBUGF(1, "Node %d is %scontained in new ring, newface is %s", n->node_id, contains ? "" : "not ", newface_outside ? "outside" : "inside" ); if ( newface_outside ) { if ( contains ) { LWDEBUGF(1, "Node %d contained in an hole of the new face", n->node_id); continue; } } else { if ( ! contains ) { LWDEBUGF(1, "Node %d not contained in the face shell", n->node_id); continue; } } updated_nodes[nodes_to_update].node_id = n->node_id; updated_nodes[nodes_to_update++].containing_face = newface.face_id; LWDEBUGF(1, "Node %d will be updated", n->node_id); } _lwt_release_nodes(nodes, numisonodes); if ( nodes_to_update ) { int ret = lwt_be_updateNodesById(topo, updated_nodes, nodes_to_update, LWT_COL_NODE_CONTAINING_FACE); if ( ret == -1 ) { lwfree( signed_edge_ids ); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -2; } } lwfree(updated_nodes); } lwfree(signed_edge_ids); lwpoly_free(shell); return newface.face_id; } /** * @param modFace can be * 0 - have two new faces replace a splitted face * 1 - modify a splitted face, adding a new one * -1 - do not check at all for face splitting * */ static LWT_ELEMID _lwt_AddEdge( LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks, int modFace ) { LWT_ISO_EDGE newedge; LWGEOM *cleangeom; edgeend span; /* start point analisys */ edgeend epan; /* end point analisys */ POINT2D p1, pn, p2; POINTARRAY *pa; LWT_ELEMID node_ids[2]; const LWPOINT *start_node_geom = NULL; const LWPOINT *end_node_geom = NULL; uint64_t num_nodes; LWT_ISO_NODE *endpoints; uint64_t i; int prev_left; int prev_right; LWT_ISO_EDGE seledge; LWT_ISO_EDGE updedge; if ( ! skipChecks ) { /* curve must be simple */ if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) ) { lwerror("SQL/MM Spatial exception - curve not simple"); return -1; } } newedge.start_node = start_node; newedge.end_node = end_node; newedge.geom = geom; newedge.face_left = -1; newedge.face_right = -1; /* TODO: should do the repeated points removal in 2D space */ cleangeom = lwgeom_remove_repeated_points( lwline_as_lwgeom(geom), 0 ); pa = lwgeom_as_lwline(cleangeom)->points; if ( pa->npoints < 2 ) { lwgeom_free(cleangeom); lwerror("Invalid edge (no two distinct vertices exist)"); return -1; } /* Initialize endpoint info (some of that ) */ span.cwFace = span.ccwFace = epan.cwFace = epan.ccwFace = -1; /* Compute azimuth of first edge end on start node */ getPoint2d_p(pa, 0, &p1); if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &pn) ) { lwgeom_free(cleangeom); lwerror("Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(&p1, &pn, &span.myaz) ) { lwgeom_free(cleangeom); lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]", p1.x, p1.y, pn.x, pn.y); return -1; } LWDEBUGF(1, "edge's start node is %g,%g", p1.x, p1.y); /* Compute azimuth of last edge end on end node */ getPoint2d_p(pa, pa->npoints-1, &p2); if ( ! _lwt_FirstDistinctVertex2D(pa, &p2, pa->npoints-1, -1, &pn) ) { lwgeom_free(cleangeom); /* This should never happen as we checked the edge while computing first edgend */ lwerror("Invalid clean edge (no two distinct vertices exist) - should not happen"); return -1; } lwgeom_free(cleangeom); if ( ! azimuth_pt_pt(&p2, &pn, &epan.myaz) ) { lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]", p2.x, p2.y, pn.x, pn.y); return -1; } LWDEBUGF(1, "edge's end node is %g,%g", p2.x, p2.y); /* * Check endpoints existence, match with Curve geometry * and get face information (if any) */ if ( start_node != end_node ) { num_nodes = 2; node_ids[0] = start_node; node_ids[1] = end_node; } else { num_nodes = 1; node_ids[0] = start_node; } endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL ); if (num_nodes == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; icontaining_face != -1 ) { if ( newedge.face_left == -1 ) { newedge.face_left = newedge.face_right = node->containing_face; } else if ( newedge.face_left != node->containing_face ) { _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - geometry crosses an edge" " (endnodes in faces %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")", newedge.face_left, node->containing_face); } } LWDEBUGF(1, "Node %d, with geom %p (looking for %d and %d)", node->node_id, node->geom, start_node, end_node); if ( node->node_id == start_node ) { start_node_geom = node->geom; } if ( node->node_id == end_node ) { end_node_geom = node->geom; } } if ( ! skipChecks ) { if ( ! start_node_geom ) { if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - non-existent node"); return -1; } else { pa = start_node_geom->point; getPoint2d_p(pa, 0, &pn); if ( ! p2d_same(&pn, &p1) ) { if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception" " - start node not geometry start point." //" - start node not geometry start point (%g,%g != %g,%g).", pn.x, pn.y, p1.x, p1.y ); return -1; } } if ( ! end_node_geom ) { if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - non-existent node"); return -1; } else { pa = end_node_geom->point; getPoint2d_p(pa, 0, &pn); if ( ! p2d_same(&pn, &p2) ) { if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception" " - end node not geometry end point." //" - end node not geometry end point (%g,%g != %g,%g).", pn.x, pn.y, p2.x, p2.y ); return -1; } } if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); if ( _lwt_CheckEdgeCrossing( topo, start_node, end_node, geom, 0 ) ) return -1; } /* ! skipChecks */ /* * All checks passed, time to prepare the new edge */ newedge.edge_id = lwt_be_getNextEdgeId( topo ); if ( newedge.edge_id == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Find adjacent edges to each endpoint */ int isclosed = start_node == end_node; int found; found = _lwt_FindAdjacentEdges( topo, start_node, &span, isclosed ? &epan : NULL, -1 ); if ( found ) { span.was_isolated = 0; newedge.next_right = span.nextCW ? span.nextCW : -newedge.edge_id; prev_left = span.nextCCW ? -span.nextCCW : newedge.edge_id; LWDEBUGF(1, "New edge %d is connected on start node, " "next_right is %d, prev_left is %d", newedge.edge_id, newedge.next_right, prev_left); if ( newedge.face_right == -1 ) { newedge.face_right = span.cwFace; } if ( newedge.face_left == -1 ) { newedge.face_left = span.ccwFace; } } else { span.was_isolated = 1; newedge.next_right = isclosed ? -newedge.edge_id : newedge.edge_id; prev_left = isclosed ? newedge.edge_id : -newedge.edge_id; LWDEBUGF(1, "New edge %d is isolated on start node, " "next_right is %d, prev_left is %d", newedge.edge_id, newedge.next_right, prev_left); } found = _lwt_FindAdjacentEdges( topo, end_node, &epan, isclosed ? &span : NULL, -1 ); if ( found ) { epan.was_isolated = 0; newedge.next_left = epan.nextCW ? epan.nextCW : newedge.edge_id; prev_right = epan.nextCCW ? -epan.nextCCW : -newedge.edge_id; LWDEBUGF(1, "New edge %d is connected on end node, " "next_left is %d, prev_right is %d", newedge.edge_id, newedge.next_left, prev_right); if ( newedge.face_right == -1 ) { newedge.face_right = span.ccwFace; } else if ( modFace != -1 && newedge.face_right != epan.ccwFace ) { /* side-location conflict */ lwerror("Side-location conflict: " "new edge starts in face" " %" LWTFMT_ELEMID " and ends in face" " %" LWTFMT_ELEMID, newedge.face_right, epan.ccwFace ); return -1; } if ( newedge.face_left == -1 ) { newedge.face_left = span.cwFace; } else if ( modFace != -1 && newedge.face_left != epan.cwFace ) { /* side-location conflict */ lwerror("Side-location conflict: " "new edge starts in face" " %" LWTFMT_ELEMID " and ends in face" " %" LWTFMT_ELEMID, newedge.face_left, epan.cwFace ); return -1; } } else { epan.was_isolated = 1; newedge.next_left = isclosed ? newedge.edge_id : -newedge.edge_id; prev_right = isclosed ? -newedge.edge_id : newedge.edge_id; LWDEBUGF(1, "New edge %d is isolated on end node, " "next_left is %d, prev_right is %d", newedge.edge_id, newedge.next_left, prev_right); } /* * If we don't have faces setup by now we must have encountered * a malformed topology (no containing_face on isolated nodes, no * left/right faces on adjacent edges or mismatching values) */ if ( newedge.face_left != newedge.face_right ) { lwerror("Left(%" LWTFMT_ELEMID ")/right(%" LWTFMT_ELEMID ")" "faces mismatch: invalid topology ?", newedge.face_left, newedge.face_right); return -1; } else if ( newedge.face_left == -1 && modFace > -1 ) { lwerror("Could not derive edge face from linked primitives:" " invalid topology ?"); return -1; } /* * Insert the new edge, and update all linking */ int ret = lwt_be_insertEdges(topo, &newedge, 1); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { lwerror("Insertion of split edge failed (no reason)"); return -1; } int updfields; /* Link prev_left to us * (if it's not us already) */ if ( llabs(prev_left) != newedge.edge_id ) { if ( prev_left > 0 ) { /* its next_left_edge is us */ updfields = LWT_COL_EDGE_NEXT_LEFT; updedge.next_left = newedge.edge_id; seledge.edge_id = prev_left; } else { /* its next_right_edge is us */ updfields = LWT_COL_EDGE_NEXT_RIGHT; updedge.next_right = newedge.edge_id; seledge.edge_id = -prev_left; } ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID, &updedge, updfields, NULL, 0); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Link prev_right to us * (if it's not us already) */ if ( llabs(prev_right) != newedge.edge_id ) { if ( prev_right > 0 ) { /* its next_left_edge is -us */ updfields = LWT_COL_EDGE_NEXT_LEFT; updedge.next_left = -newedge.edge_id; seledge.edge_id = prev_right; } else { /* its next_right_edge is -us */ updfields = LWT_COL_EDGE_NEXT_RIGHT; updedge.next_right = -newedge.edge_id; seledge.edge_id = -prev_right; } ret = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID, &updedge, updfields, NULL, 0); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* NOT IN THE SPECS... * set containing_face = null for start_node and end_node * if they where isolated * */ LWT_ISO_NODE updnode, selnode; updnode.containing_face = -1; if ( span.was_isolated ) { selnode.node_id = start_node; ret = lwt_be_updateNodes(topo, &selnode, LWT_COL_NODE_NODE_ID, &updnode, LWT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( epan.was_isolated ) { selnode.node_id = end_node; ret = lwt_be_updateNodes(topo, &selnode, LWT_COL_NODE_NODE_ID, &updnode, LWT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Check face splitting, if required */ if ( modFace > -1 ) { if ( ! isclosed && ( epan.was_isolated || span.was_isolated ) ) { LWDEBUG(1, "New edge is dangling, so it cannot split any face"); return newedge.edge_id; /* no split */ } int newface1 = -1; /* IDEA: avoid building edge ring if input is closed, which means we * know in advance it splits a face */ if ( ! modFace ) { newface1 = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 ); if ( newface1 == 0 ) { LWDEBUG(1, "New edge does not split any face"); return newedge.edge_id; /* no split */ } } int newface = _lwt_AddFaceSplit( topo, newedge.edge_id, newedge.face_left, 0 ); if ( modFace ) { if ( newface == 0 ) { LWDEBUG(1, "New edge does not split any face"); return newedge.edge_id; /* no split */ } if ( newface < 0 ) { /* face on the left is the universe face */ /* must be forming a maximal ring in universal face */ newface = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 ); if ( newface < 0 ) return newedge.edge_id; /* no split */ } else { _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 ); } } /* * Update topogeometries, if needed */ if ( newedge.face_left != 0 ) { ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left, newface, newface1); if ( ret == 0 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modFace ) { /* drop old face from the face table */ ret = lwt_be_deleteFacesById(topo, &(newedge.face_left), 1); if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } } } // end of face split checking return newedge.edge_id; } LWT_ELEMID lwt_AddEdgeModFace( LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks ) { return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 1 ); } LWT_ELEMID lwt_AddEdgeNewFaces( LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks ) { return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 ); } static LWGEOM * _lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges) { LWGEOM *outg; LWCOLLECTION *bounds; LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges ); int i, validedges = 0; for ( i=0; isrid, topo->hasZ, 0) ); } bounds = lwcollection_construct(MULTILINETYPE, topo->srid, NULL, /* gbox */ validedges, geoms); outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) ); lwcollection_release(bounds); lwfree(geoms); #if 0 { size_t sz; char *wkt = lwgeom_to_wkt(outg, WKT_EXTENDED, 2, &sz); LWDEBUGF(1, "_lwt_FaceByEdges returning area: %s", wkt); lwfree(wkt); } #endif return outg; } LWGEOM* lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid) { uint64_t numfaceedges; LWT_ISO_EDGE *edges; LWT_ISO_FACE *face; LWPOLY *out; LWGEOM *outg; uint64_t i; int fields; if (faceid == 0) { lwerror("SQL/MM Spatial exception - universal face has no geometry"); return NULL; } /* Construct the face geometry */ numfaceedges = 1; fields = LWT_COL_EDGE_GEOM | LWT_COL_EDGE_FACE_LEFT | LWT_COL_EDGE_FACE_RIGHT ; edges = lwt_be_getEdgeByFace( topo, &faceid, &numfaceedges, fields, NULL ); if (numfaceedges == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } if ( numfaceedges == 0 ) { i = 1; face = lwt_be_getFaceById(topo, &faceid, &i, LWT_COL_FACE_FACE_ID); if (i == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } if ( i == 0 ) { lwerror("SQL/MM Spatial exception - non-existent face."); return NULL; } lwfree( face ); if ( i > 1 ) { lwerror("Corrupted topology: multiple face records have face_id=%" LWTFMT_ELEMID, faceid); return NULL; } /* Face has no boundary edges, we'll return EMPTY, see * https://trac.osgeo.org/postgis/ticket/3221 */ out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0); return lwpoly_as_lwgeom(out); } outg = _lwt_FaceByEdges( topo, edges, numfaceedges ); _lwt_release_edges(edges, numfaceedges); return outg; } /* Find which edge from the "edges" set defines the next * portion of the given "ring". * * The edge might be either forward or backward. * * @param ring The ring to find definition of. * It is assumed it does not contain duplicated vertices. * @param from offset of the ring point to start looking from * @param edges array of edges to search into * @param numedges number of edges in the edges array * * @return index of the edge defining the next ring portion or * -1 if no edge was found to be part of the ring */ static int _lwt_FindNextRingEdge(const POINTARRAY *ring, int from, const LWT_ISO_EDGE *edges, int numedges) { int i; POINT2D p1; /* Get starting ring point */ getPoint2d_p(ring, from, &p1); LWDEBUGF(1, "Ring's 'from' point (%d) is %g,%g", from, p1.x, p1.y); /* find the edges defining the next portion of ring starting from * vertex "from" */ for ( i=0; igeom; POINTARRAY *epa = edge->points; POINT2D p2, pt; int match = 0; uint32_t j; /* Skip if the edge is a dangling one */ if ( isoe->face_left == isoe->face_right ) { LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID " has same face (%" LWTFMT_ELEMID ") on both sides, skipping", isoe->edge_id, isoe->face_left); continue; } if (epa->npoints < 2) { LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID " has only %"PRIu32" points", isoe->edge_id, epa->npoints); continue; } #if 0 size_t sz; LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s", isoe->edge_id, lwgeom_to_wkt(lwline_as_lwgeom(edge), WKT_EXTENDED, 2, &sz)); #endif /* ptarray_remove_repeated_points ? */ getPoint2d_p(epa, 0, &p2); LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'first' point is %g,%g", isoe->edge_id, p2.x, p2.y); LWDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y); if ( p2d_same(&p1, &p2) ) { LWDEBUG(1, "p2d_same(p1,p2) returned true"); LWDEBUGF(1, "First point of edge %" LWTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from); /* first point matches, let's check next non-equal one */ for ( j=1; jnpoints; ++j ) { getPoint2d_p(epa, j, &p2); LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'next' point %d is %g,%g", isoe->edge_id, j, p2.x, p2.y); /* we won't check duplicated edge points */ if ( p2d_same(&p1, &p2) ) continue; /* we assume there are no duplicated points in ring */ getPoint2d_p(ring, from+1, &pt); LWDEBUGF(1, "Ring's point %d is %g,%g", from+1, pt.x, pt.y); match = p2d_same(&pt, &p2); break; /* we want to check a single non-equal next vertex */ } #if POSTGIS_DEBUG_LEVEL > 0 if ( match ) { LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from+1); } else { LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID " does not match ring vertex %d", isoe->edge_id, from+1); } #endif } if ( ! match ) { LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " did not match as forward", isoe->edge_id); getPoint2d_p(epa, epa->npoints-1, &p2); LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'last' point is %g,%g", isoe->edge_id, p2.x, p2.y); if ( p2d_same(&p1, &p2) ) { LWDEBUGF(1, "Last point of edge %" LWTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from); /* last point matches, let's check next non-equal one */ for ( j=2; j<=epa->npoints; j++ ) { getPoint2d_p(epa, epa->npoints - j, &p2); LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'prev' point %d is %g,%g", isoe->edge_id, epa->npoints - j, p2.x, p2.y); /* we won't check duplicated edge points */ if ( p2d_same(&p1, &p2) ) continue; /* we assume there are no duplicated points in ring */ getPoint2d_p(ring, from+1, &pt); LWDEBUGF(1, "Ring's point %d is %g,%g", from+1, pt.x, pt.y); match = p2d_same(&pt, &p2); break; /* we want to check a single non-equal next vertex */ } } #if POSTGIS_DEBUG_LEVEL > 0 if ( match ) { LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from+1); } else { LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID " does not match ring vertex %d", isoe->edge_id, from+1); } #endif } if ( match ) return i; } return -1; } /* Reverse values in array between "from" (inclusive) * and "to" (exclusive) indexes */ static void _lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to) { LWT_ELEMID t; while (from < to) { t = ary[from]; ary[from++] = ary[to]; ary[to--] = t; } } /* Rotate values in array between "from" (inclusive) * and "to" (exclusive) indexes, so that "rotidx" is * the new value at "from" */ static void _lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx) { _lwt_ReverseElemidArray(ary, from, rotidx-1); _lwt_ReverseElemidArray(ary, rotidx, to-1); _lwt_ReverseElemidArray(ary, from, to-1); } int lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face_id, LWT_ELEMID **out ) { LWGEOM *face; LWPOLY *facepoly; LWT_ISO_EDGE *edges; uint64_t numfaceedges; int fields; uint32_t i; int nseid = 0; /* number of signed edge ids */ int prevseid; LWT_ELEMID *seid; /* signed edge ids */ /* Get list of face edges */ numfaceedges = 1; fields = LWT_COL_EDGE_EDGE_ID | LWT_COL_EDGE_GEOM | LWT_COL_EDGE_FACE_LEFT | LWT_COL_EDGE_FACE_RIGHT ; edges = lwt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields, NULL ); if (numfaceedges == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! numfaceedges ) return 0; /* no edges in output */ /* order edges by occurrence in face */ face = _lwt_FaceByEdges(topo, edges, numfaceedges); if ( ! face ) { /* _lwt_FaceByEdges should have already invoked lwerror in this case */ _lwt_release_edges(edges, numfaceedges); return -1; } if ( lwgeom_is_empty(face) ) { /* no edges in output */ _lwt_release_edges(edges, numfaceedges); lwgeom_free(face); return 0; } /* force_lhr, if the face is not the universe */ /* _lwt_FaceByEdges seems to guaranteed RHR */ /* lwgeom_force_clockwise(face); */ if ( face_id ) lwgeom_reverse_in_place(face); #if 0 { size_t sz; char *wkt = lwgeom_to_wkt(face, WKT_EXTENDED, 6, &sz); LWDEBUGF(1, "Geometry of face %" LWTFMT_ELEMID " is: %s", face_id, wkt); lwfree(wkt); } #endif facepoly = lwgeom_as_lwpoly(face); if ( ! facepoly ) { _lwt_release_edges(edges, numfaceedges); lwgeom_free(face); lwerror("Geometry of face %" LWTFMT_ELEMID " is not a polygon", face_id); return -1; } nseid = prevseid = 0; seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges ); /* for each ring of the face polygon... */ for ( i=0; inrings; ++i ) { const POINTARRAY *ring = facepoly->rings[i]; int32_t j = 0; LWT_ISO_EDGE *nextedge; LWLINE *nextline; LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints); while ( j < (int32_t) ring->npoints-1 ) { LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d", i, j); int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges); if ( edgeno == -1 ) { /* should never happen */ _lwt_release_edges(edges, numfaceedges); lwgeom_free(face); lwfree(seid); lwerror("No edge (among %d) found to be defining geometry of face %" LWTFMT_ELEMID, numfaceedges, face_id); return -1; } nextedge = &(edges[edgeno]); nextline = nextedge->geom; LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " covers ring %d from vertex %d to %d", nextedge->edge_id, i, j, j + nextline->points->npoints - 1); #if 0 { size_t sz; char *wkt = lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_EXTENDED, 6, &sz); LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s", nextedge->edge_id, wkt); lwfree(wkt); } #endif j += nextline->points->npoints - 1; /* Add next edge to the output array */ seid[nseid++] = nextedge->face_left == face_id ? nextedge->edge_id : -nextedge->edge_id; /* avoid checking again on next time turn */ nextedge->face_left = nextedge->face_right = -1; } /* now "scroll" the list of edges so that the one * with smaller absolute edge_id is first */ /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */ if ( (nseid - prevseid) > 1 ) {{ LWT_ELEMID minid = 0; int minidx = 0; LWDEBUGF(1, "Looking for smallest id among the %d edges " "composing ring %d", (nseid-prevseid), i); for ( j=prevseid; jbe_iface)); return -1; } else if ( i == 0 ) { lwerror("SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID, edge_id); return -1; } else { lwerror("Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return -1; } } LWDEBUGF(1, "lwt_ChangeEdgeGeom: " "old edge has %d points, new edge has %d points", oldedge->geom->points->npoints, geom->points->npoints); /* * e) Check StartPoint consistency */ getPoint2d_p(oldedge->geom->points, 0, &p1); getPoint2d_p(geom->points, 0, &pt); if ( ! p2d_same(&p1, &pt) ) { _lwt_release_edges(oldedge, 1); lwerror("SQL/MM Spatial exception - " "start node not geometry start point."); return -1; } /* * f) Check EndPoint consistency */ if ( oldedge->geom->points->npoints < 2 ) { _lwt_release_edges(oldedge, 1); lwerror("Corrupted topology: edge %" LWTFMT_ELEMID " has less than 2 vertices", oldedge->edge_id); return -1; } getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2); if ( geom->points->npoints < 2 ) { _lwt_release_edges(oldedge, 1); lwerror("Invalid edge: less than 2 vertices"); return -1; } getPoint2d_p(geom->points, geom->points->npoints-1, &pt); if ( ! p2d_same(&pt, &p2) ) { _lwt_release_edges(oldedge, 1); lwerror("SQL/MM Spatial exception - " "end node not geometry end point."); return -1; } /* Not in the specs: * if the edge is closed, check we didn't change winding ! * (should be part of isomorphism checking) */ if ( oldedge->start_node == oldedge->end_node ) { isclosed = 1; #if 1 /* TODO: this is actually bogus as a test */ /* check for valid edge (distinct vertices must exist) */ if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) ) { _lwt_release_edges(oldedge, 1); lwerror("Invalid edge (no two distinct vertices exist)"); return -1; } #endif if ( ptarray_isccw(oldedge->geom->points) != ptarray_isccw(geom->points) ) { _lwt_release_edges(oldedge, 1); lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y); return -1; } } if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node, oldedge->end_node, geom, edge_id ) ) { /* would have called lwerror already, leaking :( */ _lwt_release_edges(oldedge, 1); return -1; } LWDEBUG(1, "lwt_ChangeEdgeGeom: " "edge crossing check passed "); /* * Not in the specs: * Check topological isomorphism */ /* Check that the "motion range" doesn't include any node */ // 1. compute combined bbox of old and new edge GBOX mbox; /* motion box */ lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */ lwgeom_add_bbox((LWGEOM*)geom); /* just in case */ gbox_union(oldedge->geom->bbox, geom->bbox, &mbox); // 2. fetch all nodes in the combined box LWT_ISO_NODE *nodes; uint64_t numnodes; nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes, LWT_COL_NODE_ALL, 0); LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", numnodes); if (numnodes == UINT64_MAX) { _lwt_release_edges(oldedge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } // 3. if any node beside endnodes are found: if ( numnodes > ( 1 + isclosed ? 0 : 1 ) ) {{ // 3.2. bail out if any node is in one and not the other for (i=0; inode_id == oldedge->start_node ) continue; if ( n->node_id == oldedge->end_node ) continue; const POINT2D *pt = getPoint2d_cp(n->geom->point, 0); int ocont = ptarray_contains_point_partial(oldedge->geom->points, pt, isclosed, NULL) == LW_INSIDE; int ncont = ptarray_contains_point_partial(geom->points, pt, isclosed, NULL) == LW_INSIDE; if (ocont != ncont) { size_t sz; char *wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz); _lwt_release_nodes(nodes, numnodes); lwerror("Edge motion collision at %s", wkt); lwfree(wkt); /* would not necessarely reach this point */ return -1; } } }} if ( numnodes ) _lwt_release_nodes(nodes, numnodes); LWDEBUG(1, "nodes containment check passed"); /* * Check edge adjacency before * TODO: can be optimized to gather azimuths of all edge ends once */ edgeend span_pre, epan_pre; /* initialize span_pre.myaz and epan_pre.myaz with existing edge */ int res = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre, oldedge->geom, &p1, &p2); if (res) return -1; /* lwerror should have been raised */ _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre, isclosed ? &epan_pre : NULL, edge_id ); _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre, isclosed ? &span_pre : NULL, edge_id ); LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID " (last point)", span_pre.nextCW, span_pre.nextCCW, epan_pre.nextCW, epan_pre.nextCCW); /* update edge geometry */ newedge.edge_id = edge_id; newedge.geom = geom; res = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM); if (res == -1) { _lwt_release_edges(oldedge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if (!res) { _lwt_release_edges(oldedge, 1); lwerror("Unexpected error: %d edges updated when expecting 1", i); return -1; } /* * Check edge adjacency after */ edgeend span_post, epan_post; res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2); if (res) return -1; /* lwerror should have been raised */ /* initialize epan_post.myaz and epan_post.myaz */ res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2); if (res) return -1; /* lwerror should have been raised */ _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post, isclosed ? &epan_post : NULL, edge_id ); _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post, isclosed ? &span_post : NULL, edge_id ); LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID " (last point)", span_pre.nextCW, span_pre.nextCCW, epan_pre.nextCW, epan_pre.nextCCW); /* Bail out if next CW or CCW edge on start node changed */ if ( span_pre.nextCW != span_post.nextCW || span_pre.nextCCW != span_post.nextCCW ) {{ LWT_ELEMID nid = oldedge->start_node; _lwt_release_edges(oldedge, 1); lwerror("Edge changed disposition around start node %" LWTFMT_ELEMID, nid); return -1; }} /* Bail out if next CW or CCW edge on end node changed */ if ( epan_pre.nextCW != epan_post.nextCW || epan_pre.nextCCW != epan_post.nextCCW ) {{ LWT_ELEMID nid = oldedge->end_node; _lwt_release_edges(oldedge, 1); lwerror("Edge changed disposition around end node %" LWTFMT_ELEMID, nid); return -1; }} /* -- Update faces MBR of left and right faces -- TODO: think about ways to optimize this part, like see if -- the old edge geometry participated in the definition -- of the current MBR (for shrinking) or the new edge MBR -- would be larger than the old face MBR... -- */ const GBOX* oldbox = lwgeom_get_bbox(lwline_as_lwgeom(oldedge->geom)); const GBOX* newbox = lwgeom_get_bbox(lwline_as_lwgeom(geom)); if ( ! gbox_same(oldbox, newbox) ) { uint64_t facestoupdate = 0; LWT_ISO_FACE faces[2]; LWGEOM *nface1 = NULL; LWGEOM *nface2 = NULL; if ( oldedge->face_left > 0 ) { nface1 = lwt_GetFaceGeometry(topo, oldedge->face_left); if ( ! nface1 ) { lwerror("lwt_ChangeEdgeGeom could not construct face %" LWTFMT_ELEMID ", on the left of edge %" LWTFMT_ELEMID, oldedge->face_left, edge_id); return -1; } #if 0 { size_t sz; char *wkt = lwgeom_to_wkt(nface1, WKT_EXTENDED, 2, &sz); LWDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt); lwfree(wkt); } #endif lwgeom_add_bbox(nface1); faces[facestoupdate].face_id = oldedge->face_left; /* ownership left to nface */ faces[facestoupdate++].mbr = nface1->bbox; } if ( oldedge->face_right > 0 /* no need to update twice the same face.. */ && oldedge->face_right != oldedge->face_left ) { nface2 = lwt_GetFaceGeometry(topo, oldedge->face_right); if ( ! nface2 ) { lwerror("lwt_ChangeEdgeGeom could not construct face %" LWTFMT_ELEMID ", on the right of edge %" LWTFMT_ELEMID, oldedge->face_right, edge_id); return -1; } #if 0 { size_t sz; char *wkt = lwgeom_to_wkt(nface2, WKT_EXTENDED, 2, &sz); LWDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt); lwfree(wkt); } #endif lwgeom_add_bbox(nface2); faces[facestoupdate].face_id = oldedge->face_right; faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */ } LWDEBUGF(1, "%d faces to update", facestoupdate); if ( facestoupdate ) { uint64_t updatedFaces = lwt_be_updateFacesById(topo, &(faces[0]), facestoupdate); if (updatedFaces != facestoupdate) { if (nface1) lwgeom_free(nface1); if (nface2) lwgeom_free(nface2); _lwt_release_edges(oldedge, 1); if (updatedFaces == UINT64_MAX) lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); else lwerror("Unexpected error: %d faces found when expecting 1", i); return -1; } } if ( nface1 ) lwgeom_free(nface1); if ( nface2 ) lwgeom_free(nface2); } else { lwnotice("BBOX of changed edge did not change"); } LWDEBUG(1, "all done, cleaning up edges"); _lwt_release_edges(oldedge, 1); return 0; /* success */ } /* Only return CONTAINING_FACE in the node object */ static LWT_ISO_NODE * _lwt_GetIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid) { LWT_ISO_NODE *node; uint64_t n = 1; node = lwt_be_getNodeById( topo, &nid, &n, LWT_COL_NODE_CONTAINING_FACE ); if (n == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return 0; } if ( n < 1 ) { lwerror("SQL/MM Spatial exception - non-existent node"); return 0; } if ( node->containing_face == -1 ) { lwfree(node); lwerror("SQL/MM Spatial exception - not isolated node"); return 0; } return node; } int lwt_MoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid, LWPOINT *pt) { LWT_ISO_NODE *node; int ret; node = _lwt_GetIsoNode( topo, nid ); if ( ! node ) return -1; if ( lwt_be_ExistsCoincidentNode(topo, pt) ) { lwfree(node); lwerror("SQL/MM Spatial exception - coincident node"); return -1; } if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) ) { lwfree(node); lwerror("SQL/MM Spatial exception - edge crosses node."); return -1; } /* TODO: check that the new point is in the same containing face ! * See https://trac.osgeo.org/postgis/ticket/3232 */ node->node_id = nid; node->geom = pt; ret = lwt_be_updateNodesById(topo, node, 1, LWT_COL_NODE_GEOM); if ( ret == -1 ) { lwfree(node); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } lwfree(node); return 0; } int lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid) { LWT_ISO_NODE *node; int n = 1; node = _lwt_GetIsoNode( topo, nid ); if ( ! node ) return -1; n = lwt_be_deleteNodesById( topo, &nid, n ); if ( n == -1 ) { lwfree(node); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( n != 1 ) { lwfree(node); lwerror("Unexpected error: %d nodes deleted when expecting 1", n); return -1; } /* TODO: notify to caller about node being removed ? * See https://trac.osgeo.org/postgis/ticket/3231 */ lwfree(node); return 0; /* success */ } int lwt_RemIsoEdge(LWT_TOPOLOGY* topo, LWT_ELEMID id) { LWT_ISO_EDGE deledge; LWT_ISO_EDGE *edge; LWT_ELEMID nid[2]; LWT_ISO_NODE upd_node[2]; LWT_ELEMID containing_face; uint64_t n = 1; uint64_t i; edge = lwt_be_getEdgeById( topo, &id, &n, LWT_COL_EDGE_START_NODE| LWT_COL_EDGE_END_NODE | LWT_COL_EDGE_FACE_LEFT | LWT_COL_EDGE_FACE_RIGHT ); if ( ! edge ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! n ) { lwerror("SQL/MM Spatial exception - non-existent edge"); return -1; } if ( n > 1 ) { lwfree(edge); lwerror("Corrupted topology: more than a single edge have id %" LWTFMT_ELEMID, id); return -1; } if ( edge[0].face_left != edge[0].face_right ) { lwfree(edge); lwerror("SQL/MM Spatial exception - not isolated edge"); return -1; } containing_face = edge[0].face_left; nid[0] = edge[0].start_node; nid[1] = edge[0].end_node; lwfree(edge); n = 2; edge = lwt_be_getEdgeByNode( topo, nid, &n, LWT_COL_EDGE_EDGE_ID ); if ((n == UINT64_MAX) || (edge == NULL)) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i = 0; i < n; ++i) { if (edge[i].edge_id != id) { lwfree(edge); lwerror("SQL/MM Spatial exception - not isolated edge"); return -1; } } lwfree(edge); deledge.edge_id = id; n = lwt_be_deleteEdges( topo, &deledge, LWT_COL_EDGE_EDGE_ID ); if (n == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( n != 1 ) { lwerror("Unexpected error: %d edges deleted when expecting 1", n); return -1; } upd_node[0].node_id = nid[0]; upd_node[0].containing_face = containing_face; n = 1; if ( nid[1] != nid[0] ) { upd_node[1].node_id = nid[1]; upd_node[1].containing_face = containing_face; ++n; } n = lwt_be_updateNodesById(topo, upd_node, n, LWT_COL_NODE_CONTAINING_FACE); if (n == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* TODO: notify to caller about edge being removed ? * See https://trac.osgeo.org/postgis/ticket/3248 */ return 0; /* success */ } /* Used by _lwt_RemEdge to update edge face ref on healing * * @param of old face id (never 0 as you cannot remove face 0) * @param nf new face id * @return 0 on success, -1 on backend error */ static int _lwt_UpdateEdgeFaceRef( LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf) { LWT_ISO_EDGE sel_edge, upd_edge; int ret; assert( of != 0 ); /* Update face_left for all edges still referencing old face */ sel_edge.face_left = of; upd_edge.face_left = nf; ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_LEFT, &upd_edge, LWT_COL_EDGE_FACE_LEFT, NULL, 0); if ( ret == -1 ) return -1; /* Update face_right for all edges still referencing old face */ sel_edge.face_right = of; upd_edge.face_right = nf; ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_RIGHT, &upd_edge, LWT_COL_EDGE_FACE_RIGHT, NULL, 0); if ( ret == -1 ) return -1; return 0; } /* Used by _lwt_RemEdge to update node face ref on healing * * @param of old face id (never 0 as you cannot remove face 0) * @param nf new face id * @return 0 on success, -1 on backend error */ static int _lwt_UpdateNodeFaceRef( LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf) { LWT_ISO_NODE sel, upd; int ret; assert( of != 0 ); /* Update face_left for all edges still referencing old face */ sel.containing_face = of; upd.containing_face = nf; ret = lwt_be_updateNodes(topo, &sel, LWT_COL_NODE_CONTAINING_FACE, &upd, LWT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) return -1; return 0; } /* Used by lwt_RemEdgeModFace and lwt_RemEdgeNewFaces * * Returns -1 on error, identifier of the face that takes up the space * previously occupied by the removed edge if modFace is 1, identifier of * the created face (0 if none) if modFace is 0. */ static LWT_ELEMID _lwt_RemEdge( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, int modFace ) { uint64_t i, nedges, nfaces, fields; LWT_ISO_EDGE *edge = NULL; LWT_ISO_EDGE *upd_edge = NULL; LWT_ISO_EDGE upd_edge_left[2]; int nedge_left = 0; LWT_ISO_EDGE upd_edge_right[2]; int nedge_right = 0; LWT_ISO_NODE upd_node[2]; int nnode = 0; LWT_ISO_FACE *faces = NULL; LWT_ISO_FACE newface; LWT_ELEMID node_ids[2]; LWT_ELEMID face_ids[2]; int fnode_edges = 0; /* number of edges on the first node (excluded * the one being removed ) */ int lnode_edges = 0; /* number of edges on the last node (excluded * the one being removed ) */ newface.face_id = 0; i = 1; edge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL); if (!edge) { LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i); if (i == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if (i == 0) { lwerror("SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID, edge_id); return -1; } else { lwerror( "Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return -1; } } if ( ! lwt_be_checkTopoGeomRemEdge(topo, edge_id, edge->face_left, edge->face_right) ) { lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } LWDEBUG(1, "Updating next_{right,left}_face of ring edges..."); /* Update edge linking */ nedges = 0; node_ids[nedges++] = edge->start_node; if ( edge->end_node != edge->start_node ) { node_ids[nedges++] = edge->end_node; } fields = LWT_COL_EDGE_EDGE_ID | LWT_COL_EDGE_START_NODE | LWT_COL_EDGE_END_NODE | LWT_COL_EDGE_NEXT_LEFT | LWT_COL_EDGE_NEXT_RIGHT; upd_edge = lwt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields ); if (nedges == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } nedge_left = nedge_right = 0; for ( i=0; iedge_id == edge_id ) continue; if ( e->start_node == edge->start_node || e->end_node == edge->start_node ) { ++fnode_edges; } if ( e->start_node == edge->end_node || e->end_node == edge->end_node ) { ++lnode_edges; } if ( e->next_left == -edge_id ) { upd_edge_left[nedge_left].edge_id = e->edge_id; upd_edge_left[nedge_left++].next_left = edge->next_left != edge_id ? edge->next_left : edge->next_right; } else if ( e->next_left == edge_id ) { upd_edge_left[nedge_left].edge_id = e->edge_id; upd_edge_left[nedge_left++].next_left = edge->next_right != -edge_id ? edge->next_right : edge->next_left; } if ( e->next_right == -edge_id ) { upd_edge_right[nedge_right].edge_id = e->edge_id; upd_edge_right[nedge_right++].next_right = edge->next_left != edge_id ? edge->next_left : edge->next_right; } else if ( e->next_right == edge_id ) { upd_edge_right[nedge_right].edge_id = e->edge_id; upd_edge_right[nedge_right++].next_right = edge->next_right != -edge_id ? edge->next_right : edge->next_left; } } if ( nedge_left ) { LWDEBUGF(1, "updating %d 'next_left' edges", nedge_left); /* update edges in upd_edge_left set next_left */ int result = lwt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left, LWT_COL_EDGE_NEXT_LEFT); if (result == -1) { _lwt_release_edges(edge, 1); lwfree(upd_edge); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( nedge_right ) { LWDEBUGF(1, "updating %d 'next_right' edges", nedge_right); /* update edges in upd_edge_right set next_right */ int result = lwt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right, LWT_COL_EDGE_NEXT_RIGHT); if (result == -1) { _lwt_release_edges(edge, 1); lwfree(upd_edge); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } LWDEBUGF(1, "releasing %d updateable edges in %p", nedges, upd_edge); lwfree(upd_edge); /* Id of face that will take up all the space previously * taken by left and right faces of the edge */ LWT_ELEMID floodface; /* Find floodface, and update its mbr if != 0 */ if ( edge->face_left == edge->face_right ) { floodface = edge->face_right; } else { /* Two faces healed */ if ( edge->face_left == 0 || edge->face_right == 0 ) { floodface = 0; LWDEBUG(1, "floodface is universe"); } else { /* we choose right face as the face that will remain * to be symmetric with ST_AddEdgeModFace */ floodface = edge->face_right; LWDEBUGF(1, "floodface is %" LWTFMT_ELEMID, floodface); /* update mbr of floodface as union of mbr of both faces */ face_ids[0] = edge->face_left; face_ids[1] = edge->face_right; nfaces = 2; fields = LWT_COL_FACE_ALL; faces = lwt_be_getFaceById(topo, face_ids, &nfaces, fields); if (nfaces == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } GBOX *box1=NULL; GBOX *box2=NULL; for ( i=0; iface_left ) { if ( ! box1 ) box1 = faces[i].mbr; else { i = edge->face_left; _lwt_release_edges(edge, 1); _lwt_release_faces(faces, nfaces); lwerror("corrupted topology: more than 1 face have face_id=%" LWTFMT_ELEMID, i); return -1; } } else if ( faces[i].face_id == edge->face_right ) { if ( ! box2 ) box2 = faces[i].mbr; else { i = edge->face_right; _lwt_release_edges(edge, 1); _lwt_release_faces(faces, nfaces); lwerror("corrupted topology: more than 1 face have face_id=%" LWTFMT_ELEMID, i); return -1; } } else { i = faces[i].face_id; _lwt_release_edges(edge, 1); _lwt_release_faces(faces, nfaces); lwerror("Backend coding error: getFaceById returned face " "with non-requested id %" LWTFMT_ELEMID, i); return -1; } } if ( ! box1 ) { i = edge->face_left; _lwt_release_edges(edge, 1); _lwt_release_faces(faces, nfaces); lwerror("corrupted topology: no face have face_id=%" LWTFMT_ELEMID " (left face for edge %" LWTFMT_ELEMID ")", i, edge_id); return -1; } if ( ! box2 ) { i = edge->face_right; _lwt_release_edges(edge, 1); _lwt_release_faces(faces, nfaces); lwerror("corrupted topology: no face have face_id=%" LWTFMT_ELEMID " (right face for edge %" LWTFMT_ELEMID ")", i, edge_id); return -1; } gbox_merge(box2, box1); /* box1 is now the union of the two */ newface.mbr = box1; if ( modFace ) { newface.face_id = floodface; int result = lwt_be_updateFacesById(topo, &newface, 1); _lwt_release_faces(faces, 2); if (result == -1) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if (result != 1) { _lwt_release_edges(edge, 1); lwerror("Unexpected error: %d faces updated when expecting 1", i); return -1; } } else { /* New face replaces the old two faces */ newface.face_id = -1; int result = lwt_be_insertFaces(topo, &newface, 1); _lwt_release_faces(faces, 2); if (result == -1) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if (result != 1) { _lwt_release_edges(edge, 1); lwerror("Unexpected error: %d faces inserted when expecting 1", result); return -1; } floodface = newface.face_id; } } /* Update face references for edges and nodes still referencing * the removed face(s) */ if ( edge->face_left != floodface ) { if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) ) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) ) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( edge->face_right != floodface ) { if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) ) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) ) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Update topogeoms on heal */ if ( ! lwt_be_updateTopoGeomFaceHeal(topo, edge->face_right, edge->face_left, floodface) ) { _lwt_release_edges(edge, 1); lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* two faces healed */ /* Delete the edge */ int result = lwt_be_deleteEdges(topo, edge, LWT_COL_EDGE_EDGE_ID); if (result == -1) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* If any of the edge nodes remained isolated, set * containing_face = floodface */ if ( ! fnode_edges ) { upd_node[nnode].node_id = edge->start_node; upd_node[nnode].containing_face = floodface; ++nnode; } if ( edge->end_node != edge->start_node && ! lnode_edges ) { upd_node[nnode].node_id = edge->end_node; upd_node[nnode].containing_face = floodface; ++nnode; } if ( nnode ) { int result = lwt_be_updateNodesById(topo, upd_node, nnode, LWT_COL_NODE_CONTAINING_FACE); if (result == -1) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( edge->face_left != edge->face_right ) /* or there'd be no face to remove */ { LWT_ELEMID ids[2]; int nids = 0; if ( edge->face_right != floodface ) ids[nids++] = edge->face_right; if ( edge->face_left != floodface ) ids[nids++] = edge->face_left; int result = lwt_be_deleteFacesById(topo, ids, nids); if (result == -1) { _lwt_release_edges(edge, 1); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } _lwt_release_edges(edge, 1); return modFace ? floodface : newface.face_id; } LWT_ELEMID lwt_RemEdgeModFace( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id ) { return _lwt_RemEdge( topo, edge_id, 1 ); } LWT_ELEMID lwt_RemEdgeNewFace( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id ) { return _lwt_RemEdge( topo, edge_id, 0 ); } static LWT_ELEMID _lwt_HealEdges( LWT_TOPOLOGY* topo, LWT_ELEMID eid1, LWT_ELEMID eid2, int modEdge ) { LWT_ELEMID ids[2]; LWT_ELEMID commonnode = -1; int caseno = 0; LWT_ISO_EDGE *node_edges; uint64_t num_node_edges; LWT_ISO_EDGE *edges; LWT_ISO_EDGE *e1 = NULL; LWT_ISO_EDGE *e2 = NULL; LWT_ISO_EDGE newedge, updedge, seledge; uint64_t nedges, i; int e1freenode; int e2sign, e2freenode; POINTARRAY *pa; char buf[256]; char *ptr; size_t bufleft = 256; ptr = buf; /* NOT IN THE SPECS: see if the same edge is given twice.. */ if ( eid1 == eid2 ) { lwerror("Cannot heal edge %" LWTFMT_ELEMID " with itself, try with another", eid1); return -1; } ids[0] = eid1; ids[1] = eid2; nedges = 2; edges = lwt_be_getEdgeById(topo, ids, &nedges, LWT_COL_EDGE_ALL); if ((nedges == UINT64_MAX) || (edges == NULL)) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; istart_node == e1->end_node ) { _lwt_release_edges(edges, nedges); lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %" LWTFMT_ELEMID, eid1, eid2); return -1; } if ( e2->start_node == e2->end_node ) { _lwt_release_edges(edges, nedges); lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %" LWTFMT_ELEMID, eid2, eid1); return -1; } /* Find common node */ if ( e1->end_node == e2->start_node ) { commonnode = e1->end_node; caseno = 1; } else if ( e1->end_node == e2->end_node ) { commonnode = e1->end_node; caseno = 2; } /* Check if any other edge is connected to the common node, if found */ if ( commonnode != -1 ) { num_node_edges = 1; node_edges = lwt_be_getEdgeByNode( topo, &commonnode, &num_node_edges, LWT_COL_EDGE_EDGE_ID ); if (num_node_edges == UINT64_MAX) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; i 0 ) { r = snprintf(ptr, bufleft, "%s%"LWTFMT_ELEMID"", /* LWTFMT_ELEMID, */ /* EJP: changed format */ ( ptr==buf ? "" : "," ), node_edges[i].edge_id); if ( r >= (int) bufleft ) { bufleft = 0; buf[252] = '.'; buf[253] = '.'; buf[254] = '.'; buf[255] = '\0'; } else { bufleft -= r; ptr += r; } } } lwfree(node_edges); } if ( commonnode == -1 ) { if ( e1->start_node == e2->start_node ) { commonnode = e1->start_node; caseno = 3; } else if ( e1->start_node == e2->end_node ) { commonnode = e1->start_node; caseno = 4; } /* Check if any other edge is connected to the common node, if found */ if ( commonnode != -1 ) { num_node_edges = 1; node_edges = lwt_be_getEdgeByNode( topo, &commonnode, &num_node_edges, LWT_COL_EDGE_EDGE_ID ); if (num_node_edges == UINT64_MAX) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; i 0 ) { r = snprintf(ptr, bufleft, "%s%"LWTFMT_ELEMID"", /* LWTFMT_ELEMID, // EJP: changed format */ ( ptr==buf ? "" : "," ), node_edges[i].edge_id); if ( r >= (int) bufleft ) { bufleft = 0; buf[252] = '.'; buf[253] = '.'; buf[254] = '.'; buf[255] = '\0'; } else { bufleft -= r; ptr += r; } } } if ( num_node_edges ) lwfree(node_edges); } } if ( commonnode == -1 ) { _lwt_release_edges(edges, nedges); if ( ptr != buf ) { lwerror("SQL/MM Spatial exception - other edges connected (%s)", buf); } else { lwerror("SQL/MM Spatial exception - non-connected edges"); } return -1; } if ( ! lwt_be_checkTopoGeomRemNode(topo, commonnode, eid1, eid2 ) ) { _lwt_release_edges(edges, nedges); lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Construct the geometry of the new edge */ switch (caseno) { case 1: /* e1.end = e2.start */ pa = ptarray_clone_deep(e1->geom->points); //pa = ptarray_merge(pa, e2->geom->points); ptarray_append_ptarray(pa, e2->geom->points, 0); newedge.start_node = e1->start_node; newedge.end_node = e2->end_node; newedge.next_left = e2->next_left; newedge.next_right = e1->next_right; e1freenode = 1; e2freenode = -1; e2sign = 1; break; case 2: /* e1.end = e2.end */ { POINTARRAY *pa2; pa2 = ptarray_clone_deep(e2->geom->points); ptarray_reverse_in_place(pa2); pa = ptarray_clone_deep(e1->geom->points); //pa = ptarray_merge(e1->geom->points, pa); ptarray_append_ptarray(pa, pa2, 0); ptarray_free(pa2); newedge.start_node = e1->start_node; newedge.end_node = e2->start_node; newedge.next_left = e2->next_right; newedge.next_right = e1->next_right; e1freenode = 1; e2freenode = 1; e2sign = -1; break; } case 3: /* e1.start = e2.start */ pa = ptarray_clone_deep(e2->geom->points); ptarray_reverse_in_place(pa); //pa = ptarray_merge(pa, e1->geom->points); ptarray_append_ptarray(pa, e1->geom->points, 0); newedge.end_node = e1->end_node; newedge.start_node = e2->end_node; newedge.next_left = e1->next_left; newedge.next_right = e2->next_left; e1freenode = -1; e2freenode = -1; e2sign = -1; break; case 4: /* e1.start = e2.end */ pa = ptarray_clone_deep(e2->geom->points); //pa = ptarray_merge(pa, e1->geom->points); ptarray_append_ptarray(pa, e1->geom->points, 0); newedge.end_node = e1->end_node; newedge.start_node = e2->start_node; newedge.next_left = e1->next_left; newedge.next_right = e2->next_right; e1freenode = -1; e2freenode = 1; e2sign = 1; break; default: pa = NULL; e1freenode = 0; e2freenode = 0; e2sign = 0; _lwt_release_edges(edges, nedges); lwerror("Coding error: caseno=%d should never happen", caseno); break; } newedge.geom = lwline_construct(topo->srid, NULL, pa); if ( modEdge ) { /* Update data of the first edge */ newedge.edge_id = eid1; int result = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_NEXT_LEFT | LWT_COL_EDGE_NEXT_RIGHT | LWT_COL_EDGE_START_NODE | LWT_COL_EDGE_END_NODE | LWT_COL_EDGE_GEOM); if (result == -1) { lwline_free(newedge.geom); _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if (result != 1) { lwline_free(newedge.geom); if ( edges ) _lwt_release_edges(edges, nedges); lwerror("Unexpected error: %d edges updated when expecting 1", i); return -1; } } else { /* Add new edge */ newedge.edge_id = -1; newedge.face_left = e1->face_left; newedge.face_right = e1->face_right; int result = lwt_be_insertEdges(topo, &newedge, 1); if (result == -1) { lwline_free(newedge.geom); _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if (result == 0) { lwline_free(newedge.geom); _lwt_release_edges(edges, nedges); lwerror("Insertion of split edge failed (no reason)"); return -1; } } lwline_free(newedge.geom); /* -- Update next_left_edge/next_right_edge for -- any edge having them still pointing at the edge being removed -- (eid2 only when modEdge, or both otherwise) -- -- NOTE: -- e#freenode is 1 when edge# end node was the common node -- and -1 otherwise. This gives the sign of possibly found references -- to its "free" (non connected to other edge) endnode. -- e2sign is -1 if edge1 direction is opposite to edge2 direction, -- or 1 otherwise. -- */ /* update edges connected to e2's boundary from their end node */ seledge.next_left = e2freenode * eid2; updedge.next_left = e2freenode * newedge.edge_id * e2sign; int result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* update edges connected to e2's boundary from their start node */ seledge.next_right = e2freenode * eid2; updedge.next_right = e2freenode * newedge.edge_id * e2sign; result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modEdge ) { /* update edges connected to e1's boundary from their end node */ seledge.next_left = e1freenode * eid1; updedge.next_left = e1freenode * newedge.edge_id; result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* update edges connected to e1's boundary from their start node */ seledge.next_right = e1freenode * eid1; updedge.next_right = e1freenode * newedge.edge_id; result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* delete the edges (only second on modEdge or both) */ result = lwt_be_deleteEdges(topo, e2, LWT_COL_EDGE_EDGE_ID); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modEdge ) { i = lwt_be_deleteEdges(topo, e1, LWT_COL_EDGE_EDGE_ID); if (result == -1) { _lwt_release_edges(edges, nedges); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } } _lwt_release_edges(edges, nedges); /* delete the common node */ i = lwt_be_deleteNodesById( topo, &commonnode, 1 ); if (result == -1) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } /* -- -- NOT IN THE SPECS: -- Drop composition rows involving second -- edge, as the first edge took its space, -- and all affected TopoGeom have been previously checked -- for being composed by both edges. */ if ( ! lwt_be_updateTopoGeomEdgeHeal(topo, eid1, eid2, newedge.edge_id) ) { lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } return modEdge ? commonnode : newedge.edge_id; } LWT_ELEMID lwt_ModEdgeHeal( LWT_TOPOLOGY* topo, LWT_ELEMID e1, LWT_ELEMID e2 ) { return _lwt_HealEdges( topo, e1, e2, 1 ); } LWT_ELEMID lwt_NewEdgeHeal( LWT_TOPOLOGY* topo, LWT_ELEMID e1, LWT_ELEMID e2 ) { return _lwt_HealEdges( topo, e1, e2, 0 ); } LWT_ELEMID lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol) { LWT_ISO_NODE *elem; uint64_t num; int flds = LWT_COL_NODE_NODE_ID|LWT_COL_NODE_GEOM; /* geom not needed */ LWT_ELEMID id = 0; POINT2D qp; /* query point */ if ( ! getPoint2d_p(pt->point, 0, &qp) ) { lwerror("Empty query point"); return -1; } elem = lwt_be_getNodeWithinDistance2D(topo, pt, tol, &num, flds, 0); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( num ) { if ( num > 1 ) { _lwt_release_nodes(elem, num); lwerror("Two or more nodes found"); return -1; } id = elem[0].node_id; _lwt_release_nodes(elem, num); } return id; } LWT_ELEMID lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol) { LWT_ISO_EDGE *elem; uint64_t num, i; int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; /* GEOM is not needed */ LWT_ELEMID id = 0; LWGEOM *qp = lwpoint_as_lwgeom(pt); /* query point */ if ( lwgeom_is_empty(qp) ) { lwerror("Empty query point"); return -1; } elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol, &num, flds, 0); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; igeom ) { _lwt_release_edges(elem, num); lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID " has null geometry", e->edge_id); continue; } /* Should we check for intersection not being on an endpoint * as documented ? */ geom = lwline_as_lwgeom(e->geom); dist = lwgeom_mindistance2d_tolerance(geom, qp, tol); if ( dist > tol ) continue; #endif if ( id ) { _lwt_release_edges(elem, num); lwerror("Two or more edges found"); return -1; } else id = e->edge_id; } if ( num ) _lwt_release_edges(elem, num); return id; } LWT_ELEMID lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol) { LWT_ELEMID id = 0; LWT_ISO_EDGE *elem; uint64_t num, i; int flds = LWT_COL_EDGE_EDGE_ID | LWT_COL_EDGE_GEOM | LWT_COL_EDGE_FACE_LEFT | LWT_COL_EDGE_FACE_RIGHT; LWGEOM *qp = lwpoint_as_lwgeom(pt); id = lwt_be_getFaceContainingPoint(topo, pt); if ( id == -2 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( id > 0 ) { return id; } id = 0; /* or it'll be -1 for not found */ LWDEBUG(1, "No face properly contains query point," " looking for edges"); /* Not in a face, may be in universe or on edge, let's check * for distance */ /* NOTE: we never pass a tolerance of 0 to avoid ever using * ST_Within, which doesn't include endpoints matches */ elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol?tol:1e-5, &num, flds, 0); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; igeom ) { _lwt_release_edges(elem, num); lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID " has null geometry", e->edge_id); continue; } /* don't consider dangling edges */ if ( e->face_left == e->face_right ) { LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is dangling, won't consider it", e->edge_id); continue; } geom = lwline_as_lwgeom(e->geom); dist = lwgeom_mindistance2d_tolerance(geom, qp, tol); LWDEBUGF(1, "Distance from edge %" LWTFMT_ELEMID " is %g (tol=%g)", e->edge_id, dist, tol); /* we won't consider edges too far */ if ( dist > tol ) continue; if ( e->face_left == 0 ) { eface = e->face_right; } else if ( e->face_right == 0 ) { eface = e->face_left; } else { _lwt_release_edges(elem, num); lwerror("Two or more faces found"); return -1; } if ( id && id != eface ) { _lwt_release_edges(elem, num); lwerror("Two or more faces found" #if 0 /* debugging */ " (%" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")", id, eface #endif ); return -1; } else id = eface; } if ( num ) _lwt_release_edges(elem, num); return id; } /* Return the smallest delta that can perturbate * the given value */ static inline double _lwt_minToleranceDouble( double d ) { double ret = 3.6 * pow(10, - ( 15 - log10(d?d:1.0) ) ); return ret; } /* Return the smallest delta that can perturbate * the given point static inline double _lwt_minTolerancePoint2d( const POINT2D* p ) { double max = FP_ABS(p->x); if ( max < FP_ABS(p->y) ) max = FP_ABS(p->y); return _lwt_minToleranceDouble(max); } */ /* Return the smallest delta that can perturbate * the maximum absolute value of a geometry ordinate */ static double _lwt_minTolerance( LWGEOM *g ) { const GBOX* gbox; double max; double ret; gbox = lwgeom_get_bbox(g); if ( ! gbox ) return 0; /* empty */ max = FP_ABS(gbox->xmin); if ( max < FP_ABS(gbox->xmax) ) max = FP_ABS(gbox->xmax); if ( max < FP_ABS(gbox->ymin) ) max = FP_ABS(gbox->ymin); if ( max < FP_ABS(gbox->ymax) ) max = FP_ABS(gbox->ymax); ret = _lwt_minToleranceDouble(max); return ret; } #define _LWT_MINTOLERANCE( topo, geom ) ( \ topo->precision ? topo->precision : _lwt_minTolerance(geom) ) typedef struct scored_pointer_t { void *ptr; double score; } scored_pointer; static int compare_scored_pointer(const void *si1, const void *si2) { double a = ((scored_pointer *)si1)->score; double b = ((scored_pointer *)si2)->score; if ( a < b ) return -1; else if ( a > b ) return 1; else return 0; } /* * @param findFace if non-zero the code will determine which face * contains the given point (unless it is known to be NOT * isolated) * @param moved if not-null will be set to 0 if the point was added * w/out any snapping or 1 otherwise. */ static LWT_ELEMID _lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol, int findFace, int *moved) { uint64_t num, i; double mindist = FLT_MAX; LWT_ISO_NODE *nodes, *nodes2; LWT_ISO_EDGE *edges, *edges2; LWGEOM *pt = lwpoint_as_lwgeom(point); int flds; LWT_ELEMID id = 0; scored_pointer *sorted; /* Get tolerance, if 0 was given */ if (!tol) tol = _LWT_MINTOLERANCE(topo, pt); LWDEBUGG(1, pt, "Adding point"); /* -- 1. Check if any existing node is closer than the given precision -- and if so pick the closest TODO: use WithinBox2D */ flds = LWT_COL_NODE_NODE_ID | LWT_COL_NODE_GEOM; nodes = lwt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { LWDEBUGF(1, "New point is within %.15g units of %d nodes", tol, num); /* Order by distance if there are more than a single return */ if ( num > 1 ) {{ sorted= lwalloc(sizeof(scored_pointer)*num); for (i=0; inode_id, sorted[i].score); } qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer); nodes2 = lwalloc(sizeof(LWT_ISO_NODE)*num); for (i=0; igeom); double dist = lwgeom_mindistance2d(g, pt); /* TODO: move this check in the previous sort scan ... */ /* must be closer than tolerated, unless distance is zero */ if ( dist && dist >= tol ) continue; if ( ! id || dist < mindist ) { id = n->node_id; mindist = dist; } } if ( id ) { /* found an existing node */ if ( nodes ) _lwt_release_nodes(nodes, num); if ( moved ) *moved = mindist == 0 ? 0 : 1; return id; } } initGEOS(lwnotice, lwgeom_geos_error); /* -- 2. Check if any existing edge falls within tolerance -- and if so split it by a point projected on it TODO: use WithinBox2D */ flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; edges = lwt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { LWDEBUGF(1, "New point is within %.15g units of %d edges", tol, num); /* Order by distance if there are more than a single return */ if ( num > 1 ) {{ int j; sorted = lwalloc(sizeof(scored_pointer)*num); for (i=0; iedge_id, sorted[i].score); } qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer); edges2 = lwalloc(sizeof(LWT_ISO_EDGE)*num); for (j=0, i=0; igeom); } } num = j; lwfree(sorted); lwfree(edges); edges = edges2; }} for (i=0; igeom); LWGEOM *prj; int contains; LWT_ELEMID edge_id = e->edge_id; LWDEBUGF(1, "Splitting edge %" LWTFMT_ELEMID, edge_id); /* project point to line, split edge by point */ prj = lwgeom_closest_point(g, pt); if ( moved ) *moved = lwgeom_same(prj,pt) ? 0 : 1; if ( lwgeom_has_z(pt) ) {{ /* -- This is a workaround for ClosestPoint lack of Z support: -- http://trac.osgeo.org/postgis/ticket/2033 */ LWGEOM *tmp; double z; POINT4D p4d; LWPOINT *prjpt; /* add Z to "prj" */ tmp = lwgeom_force_3dz(prj); prjpt = lwgeom_as_lwpoint(tmp); getPoint4d_p(point->point, 0, &p4d); z = p4d.z; getPoint4d_p(prjpt->point, 0, &p4d); p4d.z = z; ptarray_set_point4d(prjpt->point, 0, &p4d); lwgeom_free(prj); prj = tmp; }} const POINT2D *pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0); contains = ptarray_contains_point_partial(e->geom->points, pt, 0, NULL) == LW_BOUNDARY; if ( ! contains ) {{ double snaptol; LWGEOM *snapedge; LWLINE *snapline; POINT4D p1, p2; LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " does not contain projected point to it", edge_id); /* In order to reduce the robustness issues, we'll pick * an edge that contains the projected point, if possible */ if ( i+1 < num ) { LWDEBUG(1, "But there's another to check"); lwgeom_free(prj); continue; } /* -- The tolerance must be big enough for snapping to happen -- and small enough to snap only to the projected point. -- Unfortunately ST_Distance returns 0 because it also uses -- a projected point internally, so we need another way. */ snaptol = _lwt_minTolerance(prj); snapedge = _lwt_toposnap(g, prj, snaptol); snapline = lwgeom_as_lwline(snapedge); LWDEBUGF(1, "Edge snapped with tolerance %g", snaptol); /* TODO: check if snapping did anything ? */ #if POSTGIS_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz); char *wkt2 = lwgeom_to_wkt(snapedge, WKT_EXTENDED, 15, &sz); LWDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2); lwfree(wkt1); lwfree(wkt2); } #endif /* -- Snapping currently snaps the first point below tolerance -- so may possibly move first point. See ticket #1631 */ getPoint4d_p(e->geom->points, 0, &p1); getPoint4d_p(snapline->points, 0, &p2); LWDEBUGF(1, "Edge first point is %g %g, " "snapline first point is %g %g", p1.x, p1.y, p2.x, p2.y); if ( p1.x != p2.x || p1.y != p2.y ) { LWDEBUG(1, "Snapping moved first point, re-adding it"); if ( LW_SUCCESS != ptarray_insert_point(snapline->points, &p1, 0) ) { lwgeom_free(prj); lwgeom_free(snapedge); _lwt_release_edges(edges, num); lwerror("GEOS exception on Contains: %s", lwgeom_geos_errmsg); return -1; } #if POSTGIS_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz); LWDEBUGF(1, "Tweaked snapline became %s", wkt1); lwfree(wkt1); } #endif } #if POSTGIS_DEBUG_LEVEL > 0 else { LWDEBUG(1, "Snapping did not move first point"); } #endif if ( -1 == lwt_ChangeEdgeGeom( topo, edge_id, snapline ) ) { /* TODO: should have invoked lwerror already, leaking memory */ lwgeom_free(prj); lwgeom_free(snapedge); _lwt_release_edges(edges, num); lwerror("lwt_ChangeEdgeGeom failed"); return -1; } lwgeom_free(snapedge); }} #if POSTGIS_DEBUG_LEVEL > 0 else {{ size_t sz; char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz); char *wkt2 = lwgeom_to_wkt(prj, WKT_EXTENDED, 15, &sz); LWDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2); lwfree(wkt1); lwfree(wkt2); }} #endif /* TODO: pass 1 as last argument (skipChecks) ? */ id = lwt_ModEdgeSplit( topo, edge_id, lwgeom_as_lwpoint(prj), 0 ); if ( -1 == id ) { /* TODO: should have invoked lwerror already, leaking memory */ lwgeom_free(prj); _lwt_release_edges(edges, num); lwerror("lwt_ModEdgeSplit failed"); return -1; } lwgeom_free(prj); /* * TODO: decimate the two new edges with the given tolerance ? * * the edge identifiers to decimate would be: edge_id and "id" * The problem here is that decimation of existing edges * may introduce intersections or topological inconsistencies, * for example: * * - A node may end up falling on the other side of the edge * - The decimated edge might intersect another existing edge * */ break; /* we only want to snap a single edge */ } _lwt_release_edges(edges, num); } else { /* The point is isolated, add it as such */ /* TODO: pass 1 as last argument (skipChecks) ? */ id = _lwt_AddIsoNode(topo, -1, point, 0, findFace); if ( moved ) *moved = 0; if ( -1 == id ) { /* should have invoked lwerror already, leaking memory */ lwerror("lwt_AddIsoNode failed"); return -1; } } return id; } LWT_ELEMID lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol) { return _lwt_AddPoint(topo, point, tol, 1, NULL); } /* Return identifier of an equal edge, 0 if none or -1 on error * (and lwerror gets called on error) */ static LWT_ELEMID _lwt_GetEqualEdge( LWT_TOPOLOGY *topo, LWLINE *edge ) { LWT_ELEMID id; LWT_ISO_EDGE *edges; uint64_t num, i; const GBOX *qbox = lwgeom_get_bbox( lwline_as_lwgeom(edge) ); GEOSGeometry *edgeg; const int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; edges = lwt_be_getEdgeWithinBox2D( topo, qbox, &num, flds, 0 ); if (num == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { initGEOS(lwnotice, lwgeom_geos_error); edgeg = LWGEOM2GEOS( lwline_as_lwgeom(edge), 0 ); if ( ! edgeg ) { _lwt_release_edges(edges, num); lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return -1; } for (i=0; igeom); GEOSGeometry *gg; int equals; gg = LWGEOM2GEOS( g, 0 ); if ( ! gg ) { GEOSGeom_destroy(edgeg); _lwt_release_edges(edges, num); lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return -1; } equals = GEOSEquals(gg, edgeg); GEOSGeom_destroy(gg); if ( equals == 2 ) { GEOSGeom_destroy(edgeg); _lwt_release_edges(edges, num); lwerror("GEOSEquals exception: %s", lwgeom_geos_errmsg); return -1; } if ( equals ) { id = e->edge_id; GEOSGeom_destroy(edgeg); _lwt_release_edges(edges, num); return id; } } GEOSGeom_destroy(edgeg); _lwt_release_edges(edges, num); } return 0; } /* * Add a pre-noded pre-split line edge. Used by lwt_AddLine * Return edge id, 0 if none added (empty edge), -1 on error * * @param handleFaceSplit if non-zero the code will check * if the newly added edge would split a face and if so * would create new faces accordingly. Otherwise it will * set left_face and right_face to null (-1) */ static LWT_ELEMID _lwt_AddLineEdge( LWT_TOPOLOGY* topo, LWLINE* edge, double tol, int handleFaceSplit ) { LWCOLLECTION *col; LWPOINT *start_point, *end_point; LWGEOM *tmp = 0, *tmp2; LWT_ISO_NODE *node; LWT_ELEMID nid[2]; /* start_node, end_node */ LWT_ELEMID id; /* edge id */ POINT4D p4d; uint64_t nn, i; int moved=0, mm; LWDEBUGG(1, lwline_as_lwgeom(edge), "_lwtAddLineEdge"); LWDEBUGF(1, "_lwtAddLineEdge with tolerance %g", tol); start_point = lwline_get_lwpoint(edge, 0); if ( ! start_point ) { lwnotice("Empty component of noded line"); return 0; /* must be empty */ } nid[0] = _lwt_AddPoint( topo, start_point, _lwt_minTolerance(lwpoint_as_lwgeom(start_point)), handleFaceSplit, &mm ); lwpoint_free(start_point); /* too late if lwt_AddPoint calls lwerror */ if ( nid[0] == -1 ) return -1; /* lwerror should have been called */ moved += mm; end_point = lwline_get_lwpoint(edge, edge->points->npoints-1); if ( ! end_point ) { lwerror("could not get last point of line " "after successfully getting first point !?"); return -1; } nid[1] = _lwt_AddPoint( topo, end_point, _lwt_minTolerance(lwpoint_as_lwgeom(end_point)), handleFaceSplit, &mm ); moved += mm; lwpoint_free(end_point); /* too late if lwt_AddPoint calls lwerror */ if ( nid[1] == -1 ) return -1; /* lwerror should have been called */ /* -- Added endpoints may have drifted due to tolerance, so -- we need to re-snap the edge to the new nodes before adding it */ if ( moved ) { nn = nid[0] == nid[1] ? 1 : 2; node = lwt_be_getNodeById( topo, nid, &nn, LWT_COL_NODE_NODE_ID|LWT_COL_NODE_GEOM ); if (nn == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } start_point = NULL; end_point = NULL; for (i=0; ipoint, 0, &p4d ); lwline_setPoint4d(edge, 0, &p4d); getPoint4d_p( end_point->point, 0, &p4d ); lwline_setPoint4d(edge, edge->points->npoints-1, &p4d); if ( nn ) _lwt_release_nodes(node, nn); /* make valid, after snap (to handle collapses) */ tmp = lwgeom_make_valid(lwline_as_lwgeom(edge)); col = lwgeom_as_lwcollection(tmp); if ( col ) {{ col = lwcollection_extract(col, LINETYPE); /* Check if the so-snapped edge collapsed (see #1650) */ if ( col->ngeoms == 0 ) { lwcollection_free(col); lwgeom_free(tmp); LWDEBUG(1, "Made-valid snapped edge collapsed"); return 0; } tmp2 = lwgeom_clone_deep( col->geoms[0] ); lwgeom_free(tmp); tmp = tmp2; edge = lwgeom_as_lwline(tmp); lwcollection_free(col); if ( ! edge ) { /* should never happen */ lwerror("lwcollection_extract(LINETYPE) returned a non-line?"); return -1; } }} else { edge = lwgeom_as_lwline(tmp); if ( ! edge ) { LWDEBUGF(1, "Made-valid snapped edge collapsed to %s", lwtype_name(lwgeom_get_type(tmp))); lwgeom_free(tmp); return 0; } } } /* check if the so-snapped edge _now_ exists */ id = _lwt_GetEqualEdge ( topo, edge ); LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id); if ( id == -1 ) { if ( tmp ) lwgeom_free(tmp); /* probably too late, due to internal lwerror */ return -1; } if ( id ) { if ( tmp ) lwgeom_free(tmp); /* possibly takes "edge" down with it */ return id; } /* No previously existing edge was found, we'll add one */ /* Remove consecutive vertices below given tolerance * on edge addition */ if ( tol ) {{ tmp2 = lwline_remove_repeated_points(edge, tol); LWDEBUGG(1, tmp2, "Repeated-point removed"); edge = lwgeom_as_lwline(tmp2); if ( tmp ) lwgeom_free(tmp); tmp = tmp2; /* check if the so-decimated edge _now_ exists */ id = _lwt_GetEqualEdge ( topo, edge ); LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id); if ( id == -1 ) { lwgeom_free(tmp); /* probably too late, due to internal lwerror */ return -1; } if ( id ) { lwgeom_free(tmp); /* takes "edge" down with it */ return id; } }} /* TODO: skip checks ? */ id = _lwt_AddEdge( topo, nid[0], nid[1], edge, 0, handleFaceSplit ? 1 : -1 ); LWDEBUGF(1, "lwt_AddEdgeModFace returned %" LWTFMT_ELEMID, id); if ( id == -1 ) { lwgeom_free(tmp); /* probably too late, due to internal lwerror */ return -1; } lwgeom_free(tmp); /* possibly takes "edge" down with it */ return id; } /* Simulate split-loop as it was implemented in pl/pgsql version * of TopoGeo_addLinestring */ static LWGEOM * _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes) { LWCOLLECTION *col = lwgeom_as_lwcollection(nodes); uint32_t i; LWGEOM *bg; bg = lwgeom_clone_deep(g); if ( ! col->ngeoms ) return bg; for (i=0; ingeoms; ++i) { LWGEOM *g2; g2 = lwgeom_split(bg, col->geoms[i]); lwgeom_free(bg); bg = g2; } bg->srid = nodes->srid; return bg; } static LWT_ELEMID* _lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges, int handleFaceSplit) { LWGEOM *geomsbuf[1]; LWGEOM **geoms; uint32_t ngeoms; LWGEOM *noded, *tmp; LWCOLLECTION *col; LWT_ELEMID *ids; LWT_ISO_EDGE *edges; LWT_ISO_NODE *nodes; uint64_t num, numedges = 0, numnodes = 0; uint64_t i; GBOX qbox; *nedges = -1; /* error condition, by default */ /* Get tolerance, if 0 was given */ if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line ); LWDEBUGF(1, "Working tolerance:%.15g", tol); LWDEBUGF(1, "Input line has srid=%d", line->srid); /* Remove consecutive vertices below given tolerance upfront */ if ( tol ) {{ LWLINE *clean = lwgeom_as_lwline(lwline_remove_repeated_points(line, tol)); tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */ LWDEBUGG(1, tmp, "Repeated-point removed"); }} else tmp=(LWGEOM*)line; /* 1. Self-node */ noded = lwgeom_node((LWGEOM*)tmp); if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp); if ( ! noded ) return NULL; /* should have called lwerror already */ LWDEBUGG(1, noded, "Noded"); qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) ); LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax); gbox_expand(&qbox, tol); LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g", tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax); LWGEOM **nearby = 0; int nearbyindex=0; int nearbycount = 0; /* 2. Node to edges falling within tol distance */ edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 ); if (numedges == UINT64_MAX) { lwgeom_free(noded); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes", line->points->npoints, numedges); if ( numedges ) {{ /* collect those whose distance from us is < tol */ nearbycount += numedges; nearby = lwalloc(nearbycount * sizeof(LWGEOM *)); for (i=0; igeom); LWDEBUGF(2, "Computing distance from edge %d having %d points", i, e->geom->points->npoints); double dist = lwgeom_mindistance2d(g, noded); /* must be closer than tolerated, unless distance is zero */ if ( dist && dist >= tol ) continue; nearby[nearbyindex++] = g; } LWDEBUGF(2, "Found %d lines closer than tolerance (%g)", nearbyindex, tol); if ( nearbyindex ) {{ LWCOLLECTION *col; LWGEOM *iedges; /* just an alias for col */ LWGEOM *snapped; LWGEOM *set1, *set2; LWDEBUGF(1, "Line intersects %d edges", nearbyindex); col = lwcollection_construct(COLLECTIONTYPE, topo->srid, NULL, nearbyindex, nearby); iedges = lwcollection_as_lwgeom(col); LWDEBUGG(1, iedges, "Collected edges"); LWDEBUGF(1, "Snapping noded, with srid=%d " "to interesecting edges, with srid=%d", noded->srid, iedges->srid); snapped = _lwt_toposnap(noded, iedges, tol); lwgeom_free(noded); LWDEBUGG(1, snapped, "Snapped"); LWDEBUGF(1, "Diffing snapped, with srid=%d " "and interesecting edges, with srid=%d", snapped->srid, iedges->srid); noded = lwgeom_difference(snapped, iedges); LWDEBUGG(1, noded, "Differenced"); LWDEBUGF(1, "Intersecting snapped, with srid=%d " "and interesecting edges, with srid=%d", snapped->srid, iedges->srid); set1 = lwgeom_intersection(snapped, iedges); LWDEBUGG(1, set1, "Intersected"); lwgeom_free(snapped); LWDEBUGF(1, "Linemerging set1, with srid=%d", set1->srid); set2 = lwgeom_linemerge(set1); LWDEBUGG(1, set2, "Linemerged"); LWDEBUGG(1, noded, "Noded"); lwgeom_free(set1); LWDEBUGF(1, "Unioning noded, with srid=%d " "and set2, with srid=%d", noded->srid, set2->srid); set1 = lwgeom_union(noded, set2); lwgeom_free(set2); lwgeom_free(noded); noded = set1; LWDEBUGG(1, set1, "Unioned"); /* will not release the geoms array */ lwcollection_release(col); }} }} /* 2.1. Node with existing nodes within tol * TODO: check if we should be only considering _isolated_ nodes! */ nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 ); if (numnodes == UINT64_MAX) { lwgeom_free(noded); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes); int nearbyedgecount = nearbyindex; if ( numnodes ) {{ /* collect those whose distance from us is < tol */ nearbycount = nearbyindex + numnodes; nearby = nearby ? lwrealloc(nearby, nearbycount * sizeof(LWGEOM *)) : lwalloc(nearbycount * sizeof(LWGEOM *)) ; int nn = 0; for (i=0; igeom); double dist = lwgeom_mindistance2d(g, noded); /* must be closer than tolerated, unless distance is zero */ if ( dist && dist >= tol ) continue; nearby[nearbyindex++] = g; ++nn; } }} LWDEBUGF(1, "Number of nearby elements is %d", nearbyindex); if ( numnodes ) {{ LWCOLLECTION *col; LWGEOM *elems; col = lwcollection_construct(COLLECTIONTYPE, topo->srid, NULL, nearbyindex, nearby); elems = lwcollection_as_lwgeom(col); LWDEBUGG(1, elems, "Collected nearby elements"); tmp = _lwt_toposnap(noded, elems, tol); lwgeom_free(noded); noded = tmp; LWDEBUGG(1, noded, "Elements-snapped"); /* will not release the geoms array */ lwcollection_release(col); if ( numnodes ) {{ col = lwcollection_construct(MULTIPOINTTYPE, topo->srid, NULL, nearbyindex-nearbyedgecount, nearby + nearbyedgecount); LWGEOM *inodes = lwcollection_as_lwgeom(col); tmp = _lwt_split_by_nodes(noded, inodes); lwgeom_free(noded); noded = tmp; LWDEBUGG(1, noded, "Node-split"); /* will not release the geoms array */ lwcollection_release(col); }} /* -- re-node to account for ST_Snap introduced self-intersections -- See http://trac.osgeo.org/postgis/ticket/1714 -- TODO: consider running UnaryUnion once after all noding */ tmp = lwgeom_unaryunion(noded); lwgeom_free(noded); noded = tmp; LWDEBUGG(1, noded, "Unary-unioned"); }} LWDEBUG(1, "Freeing up nearby elements"); if ( nearby ) lwfree(nearby); if ( nodes ) _lwt_release_nodes(nodes, numnodes); if ( edges ) _lwt_release_edges(edges, numedges); LWDEBUGG(1, noded, "Finally-noded"); /* 3. For each (now-noded) segment, insert an edge */ col = lwgeom_as_lwcollection(noded); if ( col ) { LWDEBUG(1, "Noded line was a collection"); geoms = col->geoms; ngeoms = col->ngeoms; } else { LWDEBUG(1, "Noded line was a single geom"); geomsbuf[0] = noded; geoms = geomsbuf; ngeoms = 1; } LWDEBUGF(1, "Line was split into %d edges", ngeoms); /* TODO: refactor to first add all nodes (re-snapping edges if * needed) and then check all edges for existing already * ( so to save a DB scan for each edge to be added ) */ ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms); num = 0; for ( i=0; isrid = noded->srid; #if POSTGIS_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz); LWDEBUGF(1, "Component %d of split line is: %s", i, wkt1); lwfree(wkt1); } #endif id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit ); LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID, id); if ( id < 0 ) { lwgeom_free(noded); lwfree(ids); return NULL; } if ( ! id ) { LWDEBUGF(1, "Component %d of split line collapsed", i); continue; } LWDEBUGF(1, "Component %d of split line is edge %" LWTFMT_ELEMID, i, id); ids[num++] = id; /* TODO: skip duplicates */ } LWDEBUGG(1, noded, "Noded before free"); lwgeom_free(noded); /* TODO: XXX remove duplicated ids if not done before */ *nedges = num; return ids; } LWT_ELEMID* lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges) { return _lwt_AddLine(topo, line, tol, nedges, 1); } LWT_ELEMID* lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges) { return _lwt_AddLine(topo, line, tol, nedges, 0); } LWT_ELEMID* lwt_AddPolygon(LWT_TOPOLOGY* topo, LWPOLY* poly, double tol, int* nfaces) { uint32_t i; *nfaces = -1; /* error condition, by default */ int num; LWT_ISO_FACE *faces; uint64_t nfacesinbox; uint64_t j; LWT_ELEMID *ids = NULL; GBOX qbox; const GEOSPreparedGeometry *ppoly; GEOSGeometry *polyg; /* Get tolerance, if 0 was given */ if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)poly ); LWDEBUGF(1, "Working tolerance:%.15g", tol); /* Add each ring as an edge */ for ( i=0; inrings; ++i ) { LWLINE *line; POINTARRAY *pa; LWT_ELEMID *eids; int nedges; pa = ptarray_clone(poly->rings[i]); line = lwline_construct(topo->srid, NULL, pa); eids = lwt_AddLine( topo, line, tol, &nedges ); if ( nedges < 0 ) { /* probably too late as lwt_AddLine invoked lwerror */ lwline_free(line); lwerror("Error adding ring %d of polygon", i); return NULL; } lwline_free(line); lwfree(eids); } /* -- Find faces covered by input polygon -- NOTE: potential snapping changed polygon edges */ qbox = *lwgeom_get_bbox( lwpoly_as_lwgeom(poly) ); gbox_expand(&qbox, tol); faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nfacesinbox, LWT_COL_FACE_ALL, 0 ); if (nfacesinbox == UINT64_MAX) { lwfree(ids); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } num = 0; if ( nfacesinbox ) { polyg = LWGEOM2GEOS(lwpoly_as_lwgeom(poly), 0); if ( ! polyg ) { _lwt_release_faces(faces, nfacesinbox); lwerror("Could not convert poly geometry to GEOS: %s", lwgeom_geos_errmsg); return NULL; } ppoly = GEOSPrepare(polyg); ids = lwalloc(sizeof(LWT_ELEMID)*nfacesinbox); for ( j=0; jface_id ); if ( ! fg ) { j = f->face_id; /* so we can destroy faces */ GEOSPreparedGeom_destroy(ppoly); GEOSGeom_destroy(polyg); lwfree(ids); _lwt_release_faces(faces, nfacesinbox); lwerror("Could not get geometry of face %" LWTFMT_ELEMID, j); return NULL; } /* check if a point on this face's surface is covered by our polygon */ fgg = LWGEOM2GEOS(fg, 0); lwgeom_free(fg); if ( ! fgg ) { GEOSPreparedGeom_destroy(ppoly); GEOSGeom_destroy(polyg); _lwt_release_faces(faces, nfacesinbox); lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return NULL; } sp = GEOSPointOnSurface(fgg); GEOSGeom_destroy(fgg); if ( ! sp ) { GEOSPreparedGeom_destroy(ppoly); GEOSGeom_destroy(polyg); _lwt_release_faces(faces, nfacesinbox); lwerror("Could not find point on face surface: %s", lwgeom_geos_errmsg); return NULL; } covers = GEOSPreparedCovers( ppoly, sp ); GEOSGeom_destroy(sp); if (covers == 2) { GEOSPreparedGeom_destroy(ppoly); GEOSGeom_destroy(polyg); _lwt_release_faces(faces, nfacesinbox); lwerror("PreparedCovers error: %s", lwgeom_geos_errmsg); return NULL; } if ( ! covers ) { continue; /* we're not composed by this face */ } /* TODO: avoid duplicates ? */ ids[num++] = f->face_id; } GEOSPreparedGeom_destroy(ppoly); GEOSGeom_destroy(polyg); _lwt_release_faces(faces, nfacesinbox); } /* possibly 0 if non face's surface point was found * to be covered by input polygon */ *nfaces = num; return ids; } /* *---- polygonizer */ /* An array of pointers to EDGERING structures */ typedef struct LWT_ISO_EDGE_TABLE_T { LWT_ISO_EDGE *edges; int size; } LWT_ISO_EDGE_TABLE; static int compare_iso_edges_by_id(const void *si1, const void *si2) { int a = ((LWT_ISO_EDGE *)si1)->edge_id; int b = ((LWT_ISO_EDGE *)si2)->edge_id; if ( a < b ) return -1; else if ( a > b ) return 1; else return 0; } static LWT_ISO_EDGE * _lwt_getIsoEdgeById(LWT_ISO_EDGE_TABLE *tab, LWT_ELEMID id) { LWT_ISO_EDGE key; key.edge_id = id; void *match = bsearch( &key, tab->edges, tab->size, sizeof(LWT_ISO_EDGE), compare_iso_edges_by_id); return match; } typedef struct LWT_EDGERING_ELEM_T { /* externally owned */ LWT_ISO_EDGE *edge; /* 0 if false, 1 if true */ int left; } LWT_EDGERING_ELEM; /* A ring of edges */ typedef struct LWT_EDGERING_T { /* Signed edge identifiers * positive ones are walked in their direction, negative ones * in the opposite direction */ LWT_EDGERING_ELEM **elems; /* Number of edges in the ring */ int size; int capacity; /* Bounding box of the ring */ GBOX *env; /* Bounding box of the ring in GEOS format (for STRTree) */ GEOSGeometry *genv; } LWT_EDGERING; #define LWT_EDGERING_INIT(a) { \ (a)->size = 0; \ (a)->capacity = 1; \ (a)->elems = lwalloc(sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \ (a)->env = NULL; \ (a)->genv = NULL; \ } #define LWT_EDGERING_PUSH(a, r) { \ if ( (a)->size + 1 > (a)->capacity ) { \ (a)->capacity *= 2; \ (a)->elems = lwrealloc((a)->elems, sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \ } \ /* lwdebug(1, "adding elem %d (%p) of edgering %p", (a)->size, (r), (a)); */ \ (a)->elems[(a)->size++] = (r); \ } #define LWT_EDGERING_CLEAN(a) { \ int i; for (i=0; i<(a)->size; ++i) { \ if ( (a)->elems[i] ) { \ /* lwdebug(1, "freeing elem %d (%p) of edgering %p", i, (a)->elems[i], (a)); */ \ lwfree((a)->elems[i]); \ } \ } \ if ( (a)->elems ) { lwfree((a)->elems); (a)->elems = NULL; } \ (a)->size = 0; \ (a)->capacity = 0; \ if ( (a)->env ) { lwfree((a)->env); (a)->env = NULL; } \ if ( (a)->genv ) { GEOSGeom_destroy((a)->genv); (a)->genv = NULL; } \ } /* An array of pointers to EDGERING structures */ typedef struct LWT_EDGERING_ARRAY_T { LWT_EDGERING **rings; int size; int capacity; GEOSSTRtree* tree; } LWT_EDGERING_ARRAY; #define LWT_EDGERING_ARRAY_INIT(a) { \ (a)->size = 0; \ (a)->capacity = 1; \ (a)->rings = lwalloc(sizeof(LWT_EDGERING *) * (a)->capacity); \ (a)->tree = NULL; \ } /* WARNING: use of 'j' is intentional, not to clash with * 'i' used in LWT_EDGERING_CLEAN */ #define LWT_EDGERING_ARRAY_CLEAN(a) { \ int j; for (j=0; j<(a)->size; ++j) { \ LWT_EDGERING_CLEAN((a)->rings[j]); \ } \ if ( (a)->capacity ) lwfree((a)->rings); \ if ( (a)->tree ) { \ GEOSSTRtree_destroy( (a)->tree ); \ (a)->tree = NULL; \ } \ } #define LWT_EDGERING_ARRAY_PUSH(a, r) { \ if ( (a)->size + 1 > (a)->capacity ) { \ (a)->capacity *= 2; \ (a)->rings = lwrealloc((a)->rings, sizeof(LWT_EDGERING *) * (a)->capacity); \ } \ (a)->rings[(a)->size++] = (r); \ } typedef struct LWT_EDGERING_POINT_ITERATOR_T { LWT_EDGERING *ring; LWT_EDGERING_ELEM *curelem; int curelemidx; int curidx; } LWT_EDGERING_POINT_ITERATOR; static int _lwt_EdgeRingIterator_next(LWT_EDGERING_POINT_ITERATOR *it, POINT2D *pt) { LWT_EDGERING_ELEM *el = it->curelem; POINTARRAY *pa; if ( ! el ) return 0; /* finished */ pa = el->edge->geom->points; int tonext = 0; LWDEBUGF(3, "iterator fetching idx %d from pa of %d points", it->curidx, pa->npoints); getPoint2d_p(pa, it->curidx, pt); if ( el->left ) { it->curidx++; if ( it->curidx >= (int) pa->npoints ) tonext = 1; } else { it->curidx--; if ( it->curidx < 0 ) tonext = 1; } if ( tonext ) { LWDEBUG(3, "iterator moving to next element"); it->curelemidx++; if ( it->curelemidx < it->ring->size ) { el = it->curelem = it->ring->elems[it->curelemidx]; it->curidx = el->left ? 0 : el->edge->geom->points->npoints - 1; } else { it->curelem = NULL; } } return 1; } /* Release return with lwfree */ static LWT_EDGERING_POINT_ITERATOR * _lwt_EdgeRingIterator_begin(LWT_EDGERING *er) { LWT_EDGERING_POINT_ITERATOR *ret = lwalloc(sizeof(LWT_EDGERING_POINT_ITERATOR)); ret->ring = er; if ( er->size ) ret->curelem = er->elems[0]; else ret->curelem = NULL; ret->curelemidx = 0; ret->curidx = ret->curelem->left ? 0 : ret->curelem->edge->geom->points->npoints - 1; return ret; } /* Identifier for a placeholder face that will be * used to mark hole rings */ #define LWT_HOLES_FACE_PLACEHOLDER INT32_MIN static int _lwt_FetchNextUnvisitedEdge(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *etab, int from) { while ( from < etab->size && etab->edges[from].face_left != -1 && etab->edges[from].face_right != -1 ) from++; return from < etab->size ? from : -1; } static LWT_ISO_EDGE * _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges) { LWT_ISO_EDGE *edge; int fields = LWT_COL_EDGE_ALL; uint64_t nelems = 1; edge = lwt_be_getEdgeWithinBox2D( topo, NULL, &nelems, fields, 0); *numedges = nelems; if (nelems == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return NULL; } return edge; } /* Update the side face of given ring edges * * Edge identifiers are signed, those with negative identifier * need to be updated their right_face, those with positive * identifier need to be updated their left_face. * * @param face identifier of the face bound by the ring * @return 0 on success, -1 on error */ static int _lwt_UpdateEdgeRingSideFace(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_ELEMID face) { LWT_ISO_EDGE *forward_edges = NULL; int forward_edges_count = 0; LWT_ISO_EDGE *backward_edges = NULL; int backward_edges_count = 0; int i, ret; /* Make a list of forward_edges and backward_edges */ forward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size); forward_edges_count = 0; backward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size); backward_edges_count = 0; for ( i=0; isize; ++i ) { LWT_EDGERING_ELEM *elem = ring->elems[i]; LWT_ISO_EDGE *edge = elem->edge; LWT_ELEMID id = edge->edge_id; if ( elem->left ) { LWDEBUGF(3, "Forward edge %d is %d", forward_edges_count, id); forward_edges[forward_edges_count].edge_id = id; forward_edges[forward_edges_count++].face_left = face; edge->face_left = face; } else { LWDEBUGF(3, "Backward edge %d is %d", forward_edges_count, id); backward_edges[backward_edges_count].edge_id = id; backward_edges[backward_edges_count++].face_right = face; edge->face_right = face; } } /* Update forward edges */ if ( forward_edges_count ) { ret = lwt_be_updateEdgesById(topo, forward_edges, forward_edges_count, LWT_COL_EDGE_FACE_LEFT); if ( ret == -1 ) { lwfree( forward_edges ); lwfree( backward_edges ); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ret != forward_edges_count ) { lwfree( forward_edges ); lwfree( backward_edges ); lwerror("Unexpected error: %d edges updated when expecting %d (forward)", ret, forward_edges_count); return -1; } } /* Update backward edges */ if ( backward_edges_count ) { ret = lwt_be_updateEdgesById(topo, backward_edges, backward_edges_count, LWT_COL_EDGE_FACE_RIGHT); if ( ret == -1 ) { lwfree( forward_edges ); lwfree( backward_edges ); lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ret != backward_edges_count ) { lwfree( forward_edges ); lwfree( backward_edges ); lwerror("Unexpected error: %d edges updated when expecting %d (backward)", ret, backward_edges_count); return -1; } } lwfree( forward_edges ); lwfree( backward_edges ); return 0; } /* * @param side 1 for left side, -1 for right side */ static LWT_EDGERING * _lwt_BuildEdgeRing(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *edges, LWT_ISO_EDGE *edge, int side) { LWT_EDGERING *ring; LWT_EDGERING_ELEM *elem; LWT_ISO_EDGE *cur; int curside; ring = lwalloc(sizeof(LWT_EDGERING)); LWT_EDGERING_INIT(ring); cur = edge; curside = side; LWDEBUGF(2, "Building rings for edge %d (side %d)", cur->edge_id, side); do { LWT_ELEMID next; elem = lwalloc(sizeof(LWT_EDGERING_ELEM)); elem->edge = cur; elem->left = ( curside == 1 ); /* Mark edge as "visited" */ if ( elem->left ) cur->face_left = LWT_HOLES_FACE_PLACEHOLDER; else cur->face_right = LWT_HOLES_FACE_PLACEHOLDER; LWT_EDGERING_PUSH(ring, elem); next = elem->left ? cur->next_left : cur->next_right; LWDEBUGF(3, " next edge is %d", next); if ( next > 0 ) curside = 1; else { curside = -1; next = -next; } cur = _lwt_getIsoEdgeById(edges, next); if ( ! cur ) { lwerror("Could not find edge with id %d", next); break; } } while (cur != edge || curside != side); LWDEBUGF(1, "Ring for edge %d has %d elems", edge->edge_id*side, ring->size); return ring; } static double _lwt_EdgeRingSignedArea(LWT_EDGERING_POINT_ITERATOR *it) { POINT2D P1; POINT2D P2; POINT2D P3; double sum = 0.0; double x0, x, y1, y2; if ( ! _lwt_EdgeRingIterator_next(it, &P1) ) return 0.0; if ( ! _lwt_EdgeRingIterator_next(it, &P2) ) return 0.0; LWDEBUG(2, "_lwt_EdgeRingSignedArea"); x0 = P1.x; while ( _lwt_EdgeRingIterator_next(it, &P3) ) { x = P2.x - x0; y1 = P3.y; y2 = P1.y; sum += x * (y2-y1); /* Move forwards! */ P1 = P2; P2 = P3; } return sum / 2.0; } /* Return 1 for true, 0 for false */ static int _lwt_EdgeRingIsCCW(LWT_EDGERING *ring) { double sa; LWDEBUGF(2, "_lwt_EdgeRingIsCCW, ring has %d elems", ring->size); LWT_EDGERING_POINT_ITERATOR *it = _lwt_EdgeRingIterator_begin(ring); sa = _lwt_EdgeRingSignedArea(it); LWDEBUGF(2, "_lwt_EdgeRingIsCCW, signed area is %g", sa); lwfree(it); if ( sa >= 0 ) return 0; else return 1; } static int _lwt_EdgeRingCrossingCount(const POINT2D *p, LWT_EDGERING_POINT_ITERATOR *it) { int cn = 0; /* the crossing number counter */ POINT2D v1, v2; #ifndef RELAX POINT2D v0; #endif if ( ! _lwt_EdgeRingIterator_next(it, &v1) ) return cn; v0 = v1; while ( _lwt_EdgeRingIterator_next(it, &v2) ) { double vt; /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.y <= p->y) && (v2.y > p->y)) /* a downward crossing */ || ((v1.y > p->y) && (v2.y <= p->y)) ) { vt = (double)(p->y - v1.y) / (v2.y - v1.y); /* P->x x < v1.x + vt * (v2.x - v1.x)) { /* a valid crossing of y=p->y right of p->x */ ++cn; } } v1 = v2; } LWDEBUGF(3, "_lwt_EdgeRingCrossingCount returning %d", cn); #ifndef RELAX if ( memcmp(&v1, &v0, sizeof(POINT2D)) ) { lwerror("_lwt_EdgeRingCrossingCount: V[n] != V[0] (%g %g != %g %g)", v1.x, v1.y, v0.x, v0.y); return -1; } #endif return cn; } /* Return 1 for true, 0 for false */ static int _lwt_EdgeRingContainsPoint(LWT_EDGERING *ring, POINT2D *p) { int cn = 0; LWT_EDGERING_POINT_ITERATOR *it = _lwt_EdgeRingIterator_begin(ring); cn = _lwt_EdgeRingCrossingCount(p, it); lwfree(it); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ } static GBOX * _lwt_EdgeRingGetBbox(LWT_EDGERING *ring) { int i; if ( ! ring->env ) { LWDEBUGF(2, "Computing GBOX for ring %p", ring); for (i=0; isize; ++i) { LWT_EDGERING_ELEM *elem = ring->elems[i]; LWLINE *g = elem->edge->geom; const GBOX *newbox = lwgeom_get_bbox(lwline_as_lwgeom(g)); if ( ! i ) ring->env = gbox_clone( newbox ); else gbox_merge( newbox, ring->env ); } } return ring->env; } static LWT_ELEMID _lwt_EdgeRingGetFace(LWT_EDGERING *ring) { LWT_EDGERING_ELEM *el = ring->elems[0]; return el->left ? el->edge->face_left : el->edge->face_right; } /* * Register a face on an edge side * * Create and register face to shell (CCW) walks, * register arbitrary negative face_id to CW rings. * * Push CCW rings to shells, CW rings to holes. * * The ownership of the "geom" and "ids" members of the * LWT_EDGERING pushed to the given LWT_EDGERING_ARRAYS * are transferred to caller. * * @param side 1 for left side, -1 for right side * * @param holes an array where holes will be pushed * * @param shells an array where shells will be pushed * * @param registered id of registered face. It will be a negative number * for holes or isolated edge strips (still registered in the face * table, but only temporary). * * @return 0 on success, -1 on error. * */ static int _lwt_RegisterFaceOnEdgeSide(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, int side, LWT_ISO_EDGE_TABLE *edges, LWT_EDGERING_ARRAY *holes, LWT_EDGERING_ARRAY *shells, LWT_ELEMID *registered) { const LWT_BE_IFACE *iface = topo->be_iface; /* this is arbitrary, could be taken as parameter */ static const int placeholder_faceid = LWT_HOLES_FACE_PLACEHOLDER; LWT_EDGERING *ring; /* Get edge ring */ ring = _lwt_BuildEdgeRing(topo, edges, edge, side); LWDEBUG(2, "Ring built, calling EdgeRingIsCCW"); /* Compute winding (CW or CCW?) */ int isccw = _lwt_EdgeRingIsCCW(ring); if ( isccw ) { /* Create new face */ LWT_ISO_FACE newface; LWDEBUGF(1, "Ring of edge %d is a shell (shell %d)", edge->edge_id * side, shells->size); newface.mbr = _lwt_EdgeRingGetBbox(ring); newface.face_id = -1; /* Insert the new face */ int ret = lwt_be_insertFaces( topo, &newface, 1 ); newface.mbr = NULL; if ( ret == -1 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ret != 1 ) { lwerror("Unexpected error: %d faces inserted when expecting 1", ret); return -1; } /* return new face_id */ *registered = newface.face_id; LWT_EDGERING_ARRAY_PUSH(shells, ring); /* update ring edges set new face_id on resp. side to *registered */ ret = _lwt_UpdateEdgeRingSideFace(topo, ring, *registered); if ( ret ) { lwerror("Errors updating edgering side face: %s", lwt_be_lastErrorMessage(iface)); return -1; } } else /* cw, so is an hole */ { LWDEBUGF(1, "Ring of edge %d is a hole (hole %d)", edge->edge_id * side, holes->size); *registered = placeholder_faceid; LWT_EDGERING_ARRAY_PUSH(holes, ring); } return 0; } static void _lwt_AccumulateCanditates(void* item, void* userdata) { LWT_EDGERING_ARRAY *candidates = userdata; LWT_EDGERING *sring = item; LWT_EDGERING_ARRAY_PUSH(candidates, sring); } static LWT_ELEMID _lwt_FindFaceContainingRing(LWT_TOPOLOGY* topo, LWT_EDGERING *ring, LWT_EDGERING_ARRAY *shells) { LWT_ELEMID foundInFace = -1; int i; const GBOX *minenv = NULL; POINT2D pt; const GBOX *testbox; GEOSGeometry *ghole; getPoint2d_p( ring->elems[0]->edge->geom->points, 0, &pt ); testbox = _lwt_EdgeRingGetBbox(ring); /* Create a GEOS Point from a vertex of the hole ring */ { LWPOINT *point = lwpoint_make2d(topo->srid, pt.x, pt.y); ghole = LWGEOM2GEOS( lwpoint_as_lwgeom(point), 1 ); lwpoint_free(point); if ( ! ghole ) { lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); return -1; } } /* Build STRtree of shell envelopes */ if ( ! shells->tree ) { static const int STRTREE_NODE_CAPACITY = 10; LWDEBUG(1, "Building STRtree"); shells->tree = GEOSSTRtree_create(STRTREE_NODE_CAPACITY); if (shells->tree == NULL) { lwerror("Could not create GEOS STRTree: %s", lwgeom_geos_errmsg); return -1; } for (i=0; isize; ++i) { LWT_EDGERING *sring = shells->rings[i]; const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring); LWDEBUGF(2, "GBOX of shell %p for edge %d is %g %g,%g %g", sring, sring->elems[0]->edge->edge_id, shellbox->xmin, shellbox->ymin, shellbox->xmax, shellbox->ymax); POINTARRAY *pa = ptarray_construct(0, 0, 2); POINT4D pt; LWLINE *diag; pt.x = shellbox->xmin; pt.y = shellbox->ymin; ptarray_set_point4d(pa, 0, &pt); pt.x = shellbox->xmax; pt.y = shellbox->ymax; ptarray_set_point4d(pa, 1, &pt); diag = lwline_construct(topo->srid, NULL, pa); /* Record just envelope in ggeom */ /* making valid, probably not needed */ sring->genv = LWGEOM2GEOS( lwline_as_lwgeom(diag), 1 ); lwline_free(diag); GEOSSTRtree_insert(shells->tree, sring->genv, sring); } LWDEBUG(1, "STRtree build completed"); } LWT_EDGERING_ARRAY candidates; LWT_EDGERING_ARRAY_INIT(&candidates); GEOSSTRtree_query(shells->tree, ghole, &_lwt_AccumulateCanditates, &candidates); LWDEBUGF(1, "Found %d candidate shells containing first point of ring's originating edge %d", candidates.size, ring->elems[0]->edge->edge_id * ( ring->elems[0]->left ? 1 : -1 ) ); /* TODO: sort candidates by bounding box size */ for (i=0; ielems[0]->edge->edge_id == ring->elems[0]->edge->edge_id ) { LWDEBUGF(1, "Shell %d is on other side of ring", _lwt_EdgeRingGetFace(sring)); continue; } /* The hole envelope cannot equal the shell envelope */ if ( gbox_same(shellbox, testbox) ) { LWDEBUGF(1, "Bbox of shell %d equals that of hole ring", _lwt_EdgeRingGetFace(sring)); continue; } /* Skip if ring box is not in shell box */ if ( ! gbox_contains_2d(shellbox, testbox) ) { LWDEBUGF(1, "Bbox of shell %d does not contain bbox of ring point", _lwt_EdgeRingGetFace(sring)); continue; } /* Skip test if a containing shell was already found * and this shell's bbox is not contained in the other */ if ( minenv && ! gbox_contains_2d(minenv, shellbox) ) { LWDEBUGF(2, "Bbox of shell %d (face %d) not contained by bbox " "of last shell found to contain the point", i, _lwt_EdgeRingGetFace(sring)); continue; } contains = _lwt_EdgeRingContainsPoint(sring, &pt); if ( contains ) { /* Continue until all shells are tested, as we want to * use the one with the smallest bounding box */ /* IDEA: sort shells by bbox size, stopping on first match */ LWDEBUGF(1, "Shell %d contains hole of edge %d", _lwt_EdgeRingGetFace(sring), ring->elems[0]->edge->edge_id); minenv = shellbox; foundInFace = _lwt_EdgeRingGetFace(sring); } } if ( foundInFace == -1 ) foundInFace = 0; candidates.size = 0; /* Avoid destroying the actual shell rings */ LWT_EDGERING_ARRAY_CLEAN(&candidates); GEOSGeom_destroy(ghole); return foundInFace; } /* * @return -1 on error (and report error), * 1 if faces beside the universal one exist * 0 otherwise */ static int _lwt_CheckFacesExist(LWT_TOPOLOGY *topo) { LWT_ISO_FACE *faces; int fields = LWT_COL_FACE_FACE_ID; uint64_t nelems = 1; GBOX qbox; qbox.xmin = qbox.ymin = -DBL_MAX; qbox.xmax = qbox.ymax = DBL_MAX; faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nelems, fields, 1); if (nelems == UINT64_MAX) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( faces ) _lwt_release_faces(faces, nelems); return nelems; } int lwt_Polygonize(LWT_TOPOLOGY* topo) { /* Fetch all edges Sort edges by edge_id Mark all edges' left and right face as -1 Iteratively: Fetch next edge with left or right face == -1 For each side with face == -1: Find ring on its side If ring is CCW: create a new face, assign to the ring edges' appropriate side If ring is CW (face needs to be same of external): assign a negative face_id the ring edges' appropriate side Now for each edge with a negative face_id on the side: Find containing face (mbr cache and all) Update with id of containing face */ const LWT_BE_IFACE *iface = topo->be_iface; LWT_ISO_EDGE *edge; int numfaces = -1; LWT_ISO_EDGE_TABLE edgetable; LWT_EDGERING_ARRAY holes, shells; int i; int err = 0; LWT_EDGERING_ARRAY_INIT(&holes); LWT_EDGERING_ARRAY_INIT(&shells); initGEOS(lwnotice, lwgeom_geos_error); /* Check if Topology already contains some Face (ignoring the Universal Face) */ numfaces = _lwt_CheckFacesExist(topo); if ( numfaces != 0 ) { if ( numfaces > 0 ) { /* Faces exist */ lwerror("Polygonize: face table is not empty."); } /* Backend error, message should have been printed already */ return -1; } edgetable.edges = _lwt_FetchAllEdges(topo, &(edgetable.size)); if ( ! edgetable.edges ) { if (edgetable.size == 0) { /* not an error: no Edges */ return 0; } /* error should have been printed already */ return -1; } /* Sort edges by ID (to allow btree searches) */ qsort(edgetable.edges, edgetable.size, sizeof(LWT_ISO_EDGE), compare_iso_edges_by_id); /* Mark all edges as unvisited */ for (i=0; iedge_id, edge->face_left, edge->face_right); if ( edge->face_left == -1 ) { err = _lwt_RegisterFaceOnEdgeSide(topo, edge, 1, &edgetable, &holes, &shells, &newface); if ( err ) break; LWDEBUGF(1, "New face on the left of edge %d is %d", edge->edge_id, newface); edge->face_left = newface; } if ( edge->face_right == -1 ) { err = _lwt_RegisterFaceOnEdgeSide(topo, edge, -1, &edgetable, &holes, &shells, &newface); if ( err ) break; LWDEBUGF(1, "New face on the right of edge %d is %d", edge->edge_id, newface); edge->face_right = newface; } } if ( err ) { _lwt_release_edges(edgetable.edges, edgetable.size); LWT_EDGERING_ARRAY_CLEAN( &holes ); LWT_EDGERING_ARRAY_CLEAN( &shells ); lwerror("Errors fetching or registering face-missing edges: %s", lwt_be_lastErrorMessage(iface)); return -1; } LWDEBUGF(1, "Found %d holes and %d shells", holes.size, shells.size); /* TODO: sort holes by pt.x, sort shells by bbox.xmin */ /* Assign shells to holes */ for (i=0; i. * ********************************************************************** * * Copyright 2011-2017 Arrival 3D, Regina Obe * **********************************************************************/ /** * @file X3D output routines. * **********************************************************************/ #include #include "liblwgeom_internal.h" #include "stringbuffer.h" /** defid is the id of the coordinate can be used to hold other elements DEF='abc' transform='' etc. **/ static int lwgeom_to_x3d3_sb(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_point_sb(const LWPOINT *point, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_line_sb(const LWLINE *line, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_triangle_sb(const LWTRIANGLE *triangle, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_multi_sb(const LWCOLLECTION *col, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_psurface_sb(const LWPSURFACE *psur, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_tin_sb(const LWTIN *tin, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int asx3d3_collection_sb(const LWCOLLECTION *col, char *srs, int precision, int opts, const char *defid, stringbuffer_t *sb); static int ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, stringbuffer_t *sb ); lwgeom/src/liblwgeom/measures3d.h0000644000176200001440000001011313773172540016545 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Nicklas Avén * Copyright 2019 Darafei Praliaskouski * **********************************************************************/ #ifndef _MEASURES3D_H #define _MEASURES3D_H 1 #include #include "measures.h" #define DOT(u, v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) #define VECTORLENGTH(v) sqrt(((v).x * (v).x) + ((v).y * (v).y) + ((v).z * (v).z)) /** Structure used in distance-calculations */ typedef struct { double distance; /*the distance between p1 and p2*/ POINT3DZ p1; POINT3DZ p2; int mode; /*the direction of looking, if thedir = -1 then we look for 3dmaxdistance and if it is 1 then we look for 3dmindistance*/ int twisted; /*To preserve the order of incoming points to match the first and second point in 3dshortest and 3dlongest line*/ double tolerance; /*the tolerance for 3ddwithin and 3ddfullywithin*/ } DISTPTS3D; typedef struct { double x, y, z; } VECTOR3D; typedef struct { POINT3DZ pop; /*Point On Plane*/ VECTOR3D pv; /*Perpendicular normal vector*/ } PLANE3D; /* Geometry returning functions */ LWGEOM *lw_dist3d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode); LWGEOM *lw_dist3d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode); /* Preprocessing functions */ int lw_dist3d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl); int lw_dist3d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl); int lw_dist3d_distribute_fast(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl); /* Brute force functions */ int lw_dist3d_pt_ptarray(POINT3DZ *p, POINTARRAY *pa, DISTPTS3D *dl); int lw_dist3d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS3D *dl); int lw_dist3d_point_line(LWPOINT *point, LWLINE *line, DISTPTS3D *dl); int lw_dist3d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS3D *dl); int lw_dist3d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS3D *dl); int lw_dist3d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS3D *dl); int lw_dist3d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS3D *dl); int lw_dist3d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS3D *dl); int lw_dist3d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS3D *dl); int lw_dist3d_poly_tri(LWPOLY *poly, LWTRIANGLE *tri, DISTPTS3D *dl); int lw_dist3d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS3D *dl); int lw_dist3d_pt_pt(POINT3DZ *p1, POINT3DZ *p2, DISTPTS3D *dl); int lw_dist3d_pt_seg(POINT3DZ *p, POINT3DZ *A, POINT3DZ *B, DISTPTS3D *dl); int lw_dist3d_pt_poly(POINT3DZ *p, LWPOLY *poly, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl); int lw_dist3d_pt_tri(POINT3DZ *p, LWTRIANGLE *tri, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl); int lw_dist3d_seg_seg(POINT3DZ *A, POINT3DZ *B, POINT3DZ *C, POINT3DZ *D, DISTPTS3D *dl); int lw_dist3d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS3D *dl); int lw_dist3d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly, PLANE3D *plane, DISTPTS3D *dl); int lw_dist3d_ptarray_tri(POINTARRAY *pa, LWTRIANGLE *tri, PLANE3D *plane, DISTPTS3D *dl); double project_point_on_plane(POINT3DZ *p, PLANE3D *pl, POINT3DZ *p0); int define_plane(POINTARRAY *pa, PLANE3D *pl); int pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane); /* Helper functions */ #endif /* !defined _MEASURES3D_H */ lwgeom/src/liblwgeom/lwgeom_geos_cluster.c0000644000176200001440000003722413773172540020551 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015-2016 Daniel Baston * **********************************************************************/ #include #include "liblwgeom.h" #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "lwgeom_geos.h" #include "lwunionfind.h" static const int STRTREE_NODE_CAPACITY = 10; /* Utility struct used to accumulate items in GEOSSTRtree_query callback */ struct QueryContext { void** items_found; uint32_t items_found_size; uint32_t num_items_found; }; /* Utility struct to keep GEOSSTRtree and associated structures to be freed after use */ struct STRTree { GEOSSTRtree* tree; GEOSGeometry** envelopes; uint32_t* geom_ids; uint32_t num_geoms; }; static struct STRTree make_strtree(void** geoms, uint32_t num_geoms, char is_lwgeom); static void destroy_strtree(struct STRTree * tree); static int union_intersecting_pairs(GEOSGeometry** geoms, uint32_t num_geoms, UNIONFIND* uf); static int combine_geometries(UNIONFIND* uf, void** geoms, uint32_t num_geoms, void*** clustersGeoms, uint32_t* num_clusters, char is_lwgeom); /* Make a minimal GEOSGeometry* whose Envelope covers the same 2D extent as * the supplied GBOX. This is faster and uses less memory than building a * five-point polygon with GBOX2GEOS. */ static GEOSGeometry* geos_envelope_surrogate(const LWGEOM* g) { if (lwgeom_is_empty(g)) return GEOSGeom_createEmptyPolygon(); if (lwgeom_get_type(g) == POINTTYPE) { const POINT2D* pt = getPoint2d_cp(lwgeom_as_lwpoint(g)->point, 0); return make_geos_point(pt->x, pt->y); } else { const GBOX* box = lwgeom_get_bbox(g); if (!box) return NULL; return make_geos_segment(box->xmin, box->ymin, box->xmax, box->ymax); } } /** Make a GEOSSTRtree that stores a pointer to a variable containing * the array index of the input geoms */ static struct STRTree make_strtree(void** geoms, uint32_t num_geoms, char is_lwgeom) { struct STRTree tree; tree.envelopes = 0; tree.num_geoms = 0; tree.geom_ids = 0; tree.tree = GEOSSTRtree_create(STRTREE_NODE_CAPACITY); if (tree.tree == NULL) { return tree; } tree.geom_ids = lwalloc(num_geoms * sizeof(uint32_t)); tree.num_geoms = num_geoms; if (is_lwgeom) { uint32_t i; tree.envelopes = lwalloc(num_geoms * sizeof(GEOSGeometry*)); for (i = 0; i < num_geoms; i++) { tree.geom_ids[i] = i; tree.envelopes[i] = geos_envelope_surrogate(geoms[i]); GEOSSTRtree_insert(tree.tree, tree.envelopes[i], &(tree.geom_ids[i])); } } else { uint32_t i; tree.envelopes = NULL; for (i = 0; i < num_geoms; i++) { tree.geom_ids[i] = i; GEOSSTRtree_insert(tree.tree, geoms[i], &(tree.geom_ids[i])); } } return tree; } /** Clean up STRTree after use */ static void destroy_strtree(struct STRTree * tree) { size_t i; GEOSSTRtree_destroy(tree->tree); if (tree->envelopes) { for (i = 0; i < tree->num_geoms; i++) { GEOSGeom_destroy(tree->envelopes[i]); } lwfree(tree->envelopes); } lwfree(tree->geom_ids); } static void query_accumulate(void* item, void* userdata) { struct QueryContext *cxt = userdata; if (!cxt->items_found) { cxt->items_found_size = 8; cxt->items_found = lwalloc(cxt->items_found_size * sizeof(void*)); } if (cxt->num_items_found >= cxt->items_found_size) { cxt->items_found_size = 2 * cxt->items_found_size; cxt->items_found = lwrealloc(cxt->items_found, cxt->items_found_size * sizeof(void*)); } cxt->items_found[cxt->num_items_found++] = item; } /* Identify intersecting geometries and mark them as being in the same set */ static int union_intersecting_pairs(GEOSGeometry** geoms, uint32_t num_geoms, UNIONFIND* uf) { uint32_t p, i; struct STRTree tree; struct QueryContext cxt = { .items_found = NULL, .num_items_found = 0, .items_found_size = 0 }; int success = LW_SUCCESS; if (num_geoms <= 1) return LW_SUCCESS; tree = make_strtree((void**) geoms, num_geoms, LW_FALSE); if (tree.tree == NULL) { destroy_strtree(&tree); return LW_FAILURE; } for (p = 0; p < num_geoms; p++) { const GEOSPreparedGeometry* prep = NULL; if (!geoms[p] || GEOSisEmpty(geoms[p])) continue; cxt.num_items_found = 0; GEOSSTRtree_query(tree.tree, geoms[p], &query_accumulate, &cxt); for (i = 0; i < cxt.num_items_found; i++) { uint32_t q = *((uint32_t*) cxt.items_found[i]); if (p != q && UF_find(uf, p) != UF_find(uf, q)) { int geos_type = GEOSGeomTypeId(geoms[p]); int geos_result; /* Don't build prepared a geometry around a Point or MultiPoint - * there are some problems in the implementation, and it's not clear * there would be a performance benefit in any case. (See #3433) */ if (geos_type != GEOS_POINT && geos_type != GEOS_MULTIPOINT) { /* Lazy initialize prepared geometry */ if (prep == NULL) { prep = GEOSPrepare(geoms[p]); } geos_result = GEOSPreparedIntersects(prep, geoms[q]); } else { geos_result = GEOSIntersects(geoms[p], geoms[q]); } if (geos_result > 1) { success = LW_FAILURE; break; } else if (geos_result) { UF_union(uf, p, q); } } } if (prep) GEOSPreparedGeom_destroy(prep); if (!success) break; } if (cxt.items_found) lwfree(cxt.items_found); destroy_strtree(&tree); return success; } /** Takes an array of GEOSGeometry* and constructs an array of GEOSGeometry*, where each element in the constructed * array is a GeometryCollection representing a set of interconnected geometries. Caller is responsible for * freeing the input array, but not for destroying the GEOSGeometry* items inside it. */ int cluster_intersecting(GEOSGeometry** geoms, uint32_t num_geoms, GEOSGeometry*** clusterGeoms, uint32_t* num_clusters) { int cluster_success; UNIONFIND* uf = UF_create(num_geoms); if (union_intersecting_pairs(geoms, num_geoms, uf) == LW_FAILURE) { UF_destroy(uf); return LW_FAILURE; } cluster_success = combine_geometries(uf, (void**) geoms, num_geoms, (void***) clusterGeoms, num_clusters, 0); UF_destroy(uf); return cluster_success; } static int dbscan_update_context(GEOSSTRtree* tree, struct QueryContext* cxt, LWGEOM** geoms, uint32_t p, double eps) { cxt->num_items_found = 0; GEOSGeometry* query_envelope; if (geoms[p]->type == POINTTYPE) { const POINT2D* pt = getPoint2d_cp(lwgeom_as_lwpoint(geoms[p])->point, 0); query_envelope = make_geos_segment( pt->x - eps, pt->y - eps, pt->x + eps, pt->y + eps ); } else { const GBOX* box = lwgeom_get_bbox(geoms[p]); query_envelope = make_geos_segment( box->xmin - eps, box->ymin - eps, box->xmax + eps, box->ymax + eps ); } if (!query_envelope) return LW_FAILURE; GEOSSTRtree_query(tree, query_envelope, &query_accumulate, cxt); GEOSGeom_destroy(query_envelope); return LW_SUCCESS; } /* Union p's cluster with q's cluster, if q is not a border point of another cluster. * Applicable to DBSCAN with minpoints > 1. */ static void union_if_available(UNIONFIND* uf, uint32_t p, uint32_t q, char* is_in_core, char* in_a_cluster) { if (in_a_cluster[q]) { /* Can we merge p's cluster with q's cluster? We can do this only * if both p and q are considered _core_ points of their respective * clusters. */ if (is_in_core[q]) { UF_union(uf, p, q); } } else { UF_union(uf, p, q); in_a_cluster[q] = LW_TRUE; } } /* An optimized DBSCAN union for the case where min_points == 1. * If min_points == 1, then we don't care how many neighbors we find; we can union clusters * on the fly, as as we go through the distance calculations. This potentially allows us * to avoid some distance computations altogether. */ static int union_dbscan_minpoints_1(LWGEOM** geoms, uint32_t num_geoms, UNIONFIND* uf, double eps, char** in_a_cluster_ret) { uint32_t p, i; struct STRTree tree; struct QueryContext cxt = { .items_found = NULL, .num_items_found = 0, .items_found_size = 0 }; int success = LW_SUCCESS; if (in_a_cluster_ret) { char* in_a_cluster = lwalloc(num_geoms * sizeof(char)); for (i = 0; i < num_geoms; i++) in_a_cluster[i] = LW_TRUE; *in_a_cluster_ret = in_a_cluster; } if (num_geoms <= 1) return LW_SUCCESS; tree = make_strtree((void**) geoms, num_geoms, LW_TRUE); if (tree.tree == NULL) { destroy_strtree(&tree); return LW_FAILURE; } for (p = 0; p < num_geoms; p++) { if (lwgeom_is_empty(geoms[p])) continue; dbscan_update_context(tree.tree, &cxt, geoms, p, eps); for (i = 0; i < cxt.num_items_found; i++) { uint32_t q = *((uint32_t*) cxt.items_found[i]); if (UF_find(uf, p) != UF_find(uf, q)) { double mindist = lwgeom_mindistance2d_tolerance(geoms[p], geoms[q], eps); if (mindist == FLT_MAX) { success = LW_FAILURE; break; } if (mindist <= eps) UF_union(uf, p, q); } } } if (cxt.items_found) lwfree(cxt.items_found); destroy_strtree(&tree); return success; } static int union_dbscan_general(LWGEOM** geoms, uint32_t num_geoms, UNIONFIND* uf, double eps, uint32_t min_points, char** in_a_cluster_ret) { uint32_t p, i; struct STRTree tree; struct QueryContext cxt = { .items_found = NULL, .num_items_found = 0, .items_found_size = 0 }; int success = LW_SUCCESS; uint32_t* neighbors; char* in_a_cluster; char* is_in_core; in_a_cluster = lwalloc(num_geoms * sizeof(char)); memset(in_a_cluster, 0, num_geoms * sizeof(char)); if (in_a_cluster_ret) *in_a_cluster_ret = in_a_cluster; /* Bail if we don't even have enough inputs to make a cluster. */ if (num_geoms <= min_points) { if (!in_a_cluster_ret) lwfree(in_a_cluster); return LW_SUCCESS; } tree = make_strtree((void**) geoms, num_geoms, LW_TRUE); if (tree.tree == NULL) { destroy_strtree(&tree); return LW_FAILURE; } is_in_core = lwalloc(num_geoms * sizeof(char)); memset(is_in_core, 0, num_geoms * sizeof(char)); neighbors = lwalloc(min_points * sizeof(uint32_t)); for (p = 0; p < num_geoms; p++) { uint32_t num_neighbors = 0; if (lwgeom_is_empty(geoms[p])) continue; dbscan_update_context(tree.tree, &cxt, geoms, p, eps); /* We didn't find enough points to do anything, even if they are all within eps. */ if (cxt.num_items_found < min_points) continue; for (i = 0; i < cxt.num_items_found; i++) { uint32_t q = *((uint32_t*) cxt.items_found[i]); if (num_neighbors >= min_points) { /* If we've already identified p as a core point, and it's already * in the same cluster in q, then there's nothing to learn by * computing the distance. */ if (UF_find(uf, p) == UF_find(uf, q)) continue; /* Similarly, if q is already identifed as a border point of another * cluster, there's no point figuring out what the distance is. */ if (in_a_cluster[q] && !is_in_core[q]) continue; } double mindist = lwgeom_mindistance2d_tolerance(geoms[p], geoms[q], eps); if (mindist == FLT_MAX) { success = LW_FAILURE; break; } if (mindist <= eps) { /* If we haven't hit min_points yet, we don't know if we can union p and q. * Just set q aside for now. */ if (num_neighbors < min_points) { neighbors[num_neighbors++] = q; /* If we just hit min_points, we can now union all of the neighbor geometries * we've been saving. */ if (num_neighbors == min_points) { uint32_t j; is_in_core[p] = LW_TRUE; in_a_cluster[p] = LW_TRUE; for (j = 0; j < num_neighbors; j++) { union_if_available(uf, p, neighbors[j], is_in_core, in_a_cluster); } } } else { /* If we're above min_points, no need to store our neighbors, just go ahead * and union them now. This may allow us to cut out some distance * computations. */ union_if_available(uf, p, q, is_in_core, in_a_cluster); } } } if (!success) break; } lwfree(neighbors); lwfree(is_in_core); /* Free in_a_cluster if we're not giving it to our caller */ if (!in_a_cluster_ret) lwfree(in_a_cluster); if (cxt.items_found) lwfree(cxt.items_found); destroy_strtree(&tree); return success; } int union_dbscan(LWGEOM** geoms, uint32_t num_geoms, UNIONFIND* uf, double eps, uint32_t min_points, char** in_a_cluster_ret) { if (min_points <= 1) return union_dbscan_minpoints_1(geoms, num_geoms, uf, eps, in_a_cluster_ret); else return union_dbscan_general(geoms, num_geoms, uf, eps, min_points, in_a_cluster_ret); } /** Takes an array of LWGEOM* and constructs an array of LWGEOM*, where each element in the constructed array is a * GeometryCollection representing a set of geometries separated by no more than the specified tolerance. Caller is * responsible for freeing the input array, but not the LWGEOM* items inside it. */ int cluster_within_distance(LWGEOM** geoms, uint32_t num_geoms, double tolerance, LWGEOM*** clusterGeoms, uint32_t* num_clusters) { int cluster_success; UNIONFIND* uf = UF_create(num_geoms); if (union_dbscan(geoms, num_geoms, uf, tolerance, 1, NULL) == LW_FAILURE) { UF_destroy(uf); return LW_FAILURE; } cluster_success = combine_geometries(uf, (void**) geoms, num_geoms, (void***) clusterGeoms, num_clusters, 1); UF_destroy(uf); return cluster_success; } /** Uses a UNIONFIND to identify the set with which each input geometry is associated, and groups the geometries into * GeometryCollections. Supplied geometry array may be of either LWGEOM* or GEOSGeometry*; is_lwgeom is used to * identify which. Caller is responsible for freeing input geometry array but not the items contained within it. */ static int combine_geometries(UNIONFIND* uf, void** geoms, uint32_t num_geoms, void*** clusterGeoms, uint32_t* num_clusters, char is_lwgeom) { size_t i, j, k; /* Combine components of each cluster into their own GeometryCollection */ *num_clusters = uf->num_clusters; *clusterGeoms = lwalloc(*num_clusters * sizeof(void*)); void** geoms_in_cluster = lwalloc(num_geoms * sizeof(void*)); uint32_t* ordered_components = UF_ordered_by_cluster(uf); for (i = 0, j = 0, k = 0; i < num_geoms; i++) { geoms_in_cluster[j++] = geoms[ordered_components[i]]; /* Is this the last geometry in the component? */ if ((i == num_geoms - 1) || (UF_find(uf, ordered_components[i]) != UF_find(uf, ordered_components[i+1]))) { if (k >= uf->num_clusters) { /* Should not get here - it means that we have more clusters than uf->num_clusters thinks we should. */ return LW_FAILURE; } if (is_lwgeom) { LWGEOM** components = lwalloc(j * sizeof(LWGEOM*)); memcpy(components, geoms_in_cluster, j * sizeof(LWGEOM*)); (*clusterGeoms)[k++] = lwcollection_construct(COLLECTIONTYPE, components[0]->srid, NULL, j, (LWGEOM**) components); } else { int srid = GEOSGetSRID(((GEOSGeometry**) geoms_in_cluster)[0]); GEOSGeometry* combined = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, (GEOSGeometry**) geoms_in_cluster, j); GEOSSetSRID(combined, srid); (*clusterGeoms)[k++] = combined; } j = 0; } } lwfree(geoms_in_cluster); lwfree(ordered_components); return LW_SUCCESS; } lwgeom/src/liblwgeom/lwgeom_log.h0000644000176200001440000000766413773172540016646 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2008 Paul Ramsey * Copyright 2007-2008 Mark Cave-Ayland * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef LWGEOM_LOG_H #define LWGEOM_LOG_H 1 #include /* * Debug macros */ #if POSTGIS_DEBUG_LEVEL > 0 /* Display a notice at the given debug level */ #define LWDEBUG(level, msg) \ do { \ if (POSTGIS_DEBUG_LEVEL >= level) \ lwdebug(level, "[%s:%s:%d] " msg, __FILE__, __func__, __LINE__); \ } while (0); /* Display a formatted notice at the given debug level * (like printf, with variadic arguments) */ #define LWDEBUGF(level, msg, ...) \ do { \ if (POSTGIS_DEBUG_LEVEL >= level) \ lwdebug(level, "[%s:%s:%d] " msg, \ __FILE__, __func__, __LINE__, __VA_ARGS__); \ } while (0); /* Display a notice and a WKT representation of a geometry * at the given debug level */ #define LWDEBUGG(level, geom, msg) \ if (POSTGIS_DEBUG_LEVEL >= level) \ do { \ size_t sz; \ char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, &sz); \ /* char *wkt = lwgeom_to_hexwkb(geom, WKT_EXTENDED, &sz); */ \ LWDEBUGF(level, msg ": %s", wkt); \ lwfree(wkt); \ } while (0); /* Display a formatted notice and a WKT representation of a geometry * at the given debug level */ #define LWDEBUGGF(level, geom, fmt, ...) \ if (POSTGIS_DEBUG_LEVEL >= level) \ do { \ size_t sz; \ char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, &sz); \ /* char *wkt = lwgeom_to_hexwkb(geom, WKT_EXTENDED, &sz); */ \ LWDEBUGF(level, fmt ": %s", __VA_ARGS__, wkt); \ lwfree(wkt); \ } while (0); #else /* POSTGIS_DEBUG_LEVEL <= 0 */ /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define LWDEBUG(level, msg) \ ((void) 0) /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define LWDEBUGF(level, msg, ...) \ ((void) 0) /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define LWDEBUGG(level, geom, msg) \ ((void) 0) /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define LWDEBUGGF(level, geom, fmt, ...) \ ((void) 0) #endif /* POSTGIS_DEBUG_LEVEL <= 0 */ /** * Write a notice out to the notice handler. * * Uses standard printf() substitutions. * Use for messages you always want output. * For debugging, use LWDEBUG() or LWDEBUGF(). * @ingroup logging */ void lwnotice(const char *fmt, ...); /** * Write a notice out to the error handler. * * Uses standard printf() substitutions. * Use for errors you always want output. * For debugging, use LWDEBUG() or LWDEBUGF(). * @ingroup logging */ void lwerror(const char *fmt, ...); /** * Write a debug message out. * Don't call this function directly, use the * macros, LWDEBUG() or LWDEBUGF(), for * efficiency. * @ingroup logging */ void lwdebug(int level, const char *fmt, ...); #endif /* LWGEOM_LOG_H */ lwgeom/src/liblwgeom/lwmpoint.c0000644000176200001440000000517713773172540016354 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" void lwmpoint_release(LWMPOINT *lwmpoint) { lwgeom_release(lwmpoint_as_lwgeom(lwmpoint)); } LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm) { LWMPOINT *ret = (LWMPOINT*)lwcollection_construct_empty(MULTIPOINTTYPE, srid, hasz, hasm); return ret; } LWMPOINT* lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj) { LWDEBUG(4, "Called"); return (LWMPOINT*)lwcollection_add_lwgeom((LWCOLLECTION*)mobj, (LWGEOM*)obj); } LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa) { uint32_t i; int hasz = ptarray_has_z(pa); int hasm = ptarray_has_m(pa); LWMPOINT *ret = (LWMPOINT*)lwcollection_construct_empty(MULTIPOINTTYPE, srid, hasz, hasm); for ( i = 0; i < pa->npoints; i++ ) { LWPOINT *lwp; POINT4D p; getPoint4d_p(pa, i, &p); lwp = lwpoint_make(srid, hasz, hasm, &p); lwmpoint_add_lwpoint(ret, lwp); } return ret; } void lwmpoint_free(LWMPOINT *mpt) { uint32_t i; if ( ! mpt ) return; if ( mpt->bbox ) lwfree(mpt->bbox); for ( i = 0; i < mpt->ngeoms; i++ ) if ( mpt->geoms && mpt->geoms[i] ) lwpoint_free(mpt->geoms[i]); if ( mpt->geoms ) lwfree(mpt->geoms); lwfree(mpt); } LWMPOINT* lwmpoint_from_lwgeom(const LWGEOM *g) { LWPOINTITERATOR* it = lwpointiterator_create(g); int has_z = lwgeom_has_z(g); int has_m = lwgeom_has_m(g); LWMPOINT* result = lwmpoint_construct_empty(g->srid, has_z, has_m); POINT4D p; while(lwpointiterator_next(it, &p)) { LWPOINT* lwp = lwpoint_make(g->srid, has_z, has_m, &p); lwmpoint_add_lwpoint(result, lwp); } lwpointiterator_destroy(it); return result; } lwgeom/src/liblwgeom/lwgeodetic.h0000644000176200001440000001452313773172540016631 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #ifndef _LWGEODETIC_H #define _LWGEODETIC_H 1 /* For NAN */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #ifndef NAN #define NAN 0.0/0.0 #endif /* Override tolerance for geodetic */ #ifdef FP_TOLERANCE #undef FP_TOLERANCE #define FP_TOLERANCE 1e-14 #endif extern int gbox_geocentric_slow; #define POW2(x) ((x)*(x)) /** * Point in spherical coordinates on the world. Units of radians. */ typedef struct { double lon; double lat; } GEOGRAPHIC_POINT; /** * Two-point great circle segment from a to b. */ typedef struct { GEOGRAPHIC_POINT start; GEOGRAPHIC_POINT end; } GEOGRAPHIC_EDGE; /** * Holder for sorting points in distance algorithm */ typedef struct { double measure; uint32_t index; } DISTANCE_ORDER; /** * Conversion functions */ #define deg2rad(d) (M_PI * (d) / 180.0) #define rad2deg(r) (180.0 * (r) / M_PI) /** * Bitmask elements for edge_intersects() return value. */ #define PIR_NO_INTERACT 0x00 #define PIR_INTERSECTS 0x01 #define PIR_COLINEAR 0x02 #define PIR_A_TOUCH_RIGHT 0x04 #define PIR_A_TOUCH_LEFT 0x08 #define PIR_B_TOUCH_RIGHT 0x10 #define PIR_B_TOUCH_LEFT 0x20 /* * Geodetic calculations */ void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p); void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g); void robust_cross_product(const GEOGRAPHIC_POINT *p, const GEOGRAPHIC_POINT *q, POINT3D *a); void x_to_z(POINT3D *p); void y_to_z(POINT3D *p); int edge_point_on_plane(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_point_in_cone(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_contains_coplanar_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_contains_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); double z_to_latitude(double z, int top); int clairaut_cartesian(const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom); int clairaut_geographic(const GEOGRAPHIC_POINT *start, const GEOGRAPHIC_POINT *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom); double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e); double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e); int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n); int edge_calculate_gbox_slow(const GEOGRAPHIC_EDGE *e, GBOX *gbox); int edge_calculate_gbox(const POINT3D *A1, const POINT3D *A2, GBOX *gbox); int edge_intersection(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g); uint32_t edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2); double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest); double edge_distance_to_edge(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2); void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g); int ptarray_contains_point_sphere(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test); int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test); int lwpoly_covers_lwpoly(const LWPOLY *lwpoly1, const LWPOLY *lwpoly2); int lwpoly_covers_pointarray(const LWPOLY* lwpoly, const POINTARRAY* pta); int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line); int lwline_covers_lwline(const LWLINE* lwline1, const LWLINE* lwline2); int lwline_covers_lwpoint(const LWLINE* lwline, const LWPOINT* lwpoint); int lwpoly_intersects_line(const LWPOLY* lwpoly, const POINTARRAY* line); int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside); int ptarray_point_in_ring(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test); double ptarray_area_sphere(const POINTARRAY *pa); double latitude_degrees_normalize(double lat); double longitude_degrees_normalize(double lon); double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s); int geographic_point_equals(const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2); int crosses_dateline(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e); void point_shift(GEOGRAPHIC_POINT *p, double shift); double longitude_radians_normalize(double lon); double latitude_radians_normalize(double lat); void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n); void vector_scale(POINT3D *a, double s); double vector_angle(const POINT3D* v1, const POINT3D* v2); void vector_rotate(const POINT3D* v1, const POINT3D* v2, double angle, POINT3D* n); void normalize(POINT3D *p); void unit_normal(const POINT3D *P1, const POINT3D *P2, POINT3D *normal); double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d); void ll2cart(const POINT2D *g, POINT3D *p); /* ** Prototypes for spheroid functions. */ double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid); double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid); int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g); #endif /* _LWGEODETIC_H */ /** * Notes for rewrite * * Define separate POINT types for 2-d-points-in-radiands and 3-d-points-in-geocentric * Maintain consistent units (radians?) throughout all calculations * Put an index pointer onto LWGEOM itself, and cache the indexed LWGEOM instead of a bare tree * only primitive objects should get a tree */ lwgeom/src/liblwgeom/lwout_wkt.c0000644000176200001440000004630513773172540016540 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "stringbuffer.h" static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant); /* * ISO format uses both Z and M qualifiers. * Extended format only uses an M qualifier for 3DM variants, where it is not * clear what the third dimension represents. * SFSQL format never has more than two dimensions, so no qualifiers. */ static void dimension_qualifiers_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, uint8_t variant) { /* Extended WKT: POINTM(0 0 0) */ #if 0 if ( (variant & WKT_EXTENDED) && ! (variant & WKT_IS_CHILD) && FLAGS_GET_M(geom->flags) && (!FLAGS_GET_Z(geom->flags)) ) #else if ( (variant & WKT_EXTENDED) && FLAGS_GET_M(geom->flags) && (!FLAGS_GET_Z(geom->flags)) ) #endif { stringbuffer_append(sb, "M"); /* "M" */ return; } /* ISO WKT: POINT ZM (0 0 0 0) */ if ( (variant & WKT_ISO) && (FLAGS_NDIMS(geom->flags) > 2) ) { stringbuffer_append(sb, " "); if ( FLAGS_GET_Z(geom->flags) ) stringbuffer_append(sb, "Z"); if ( FLAGS_GET_M(geom->flags) ) stringbuffer_append(sb, "M"); stringbuffer_append(sb, " "); } } /* * Write an empty token out, padding with a space if * necessary. */ static void empty_to_wkt_sb(stringbuffer_t *sb) { if ( ! strchr(" ,(", stringbuffer_lastchar(sb)) ) /* "EMPTY" */ { stringbuffer_append(sb, " "); } stringbuffer_append(sb, "EMPTY"); } /* * Point array is a list of coordinates. Depending on output mode, * we may suppress some dimensions. ISO and Extended formats include * all dimensions. Standard OGC output only includes X/Y coordinates. */ static void ptarray_to_wkt_sb(const POINTARRAY *ptarray, stringbuffer_t *sb, int precision, uint8_t variant) { /* OGC only includes X/Y */ uint32_t dimensions = 2; uint32_t i, j; static size_t buffer_size = 128; char coord[buffer_size]; /* ISO and extended formats include all dimensions */ if ( variant & ( WKT_ISO | WKT_EXTENDED ) ) dimensions = FLAGS_NDIMS(ptarray->flags); /* Opening paren? */ if ( ! (variant & WKT_NO_PARENS) ) stringbuffer_append(sb, "("); /* Digits and commas */ for (i = 0; i < ptarray->npoints; i++) { double *dbl_ptr = (double*)getPoint_internal(ptarray, i); /* Commas before ever coord but the first */ if ( i > 0 ) stringbuffer_append(sb, ","); for (j = 0; j < dimensions; j++) { /* Spaces before every ordinate but the first */ if ( j > 0 ) stringbuffer_append(sb, " "); lwprint_double(dbl_ptr[j], precision, coord, buffer_size); stringbuffer_append(sb, coord); } } /* Closing paren? */ if ( ! (variant & WKT_NO_PARENS) ) stringbuffer_append(sb, ")"); } /* * A four-dimensional point will have different outputs depending on variant. * ISO: POINT ZM (0 0 0 0) * Extended: POINT(0 0 0 0) * OGC: POINT(0 0) * A three-dimensional m-point will have different outputs too. * ISO: POINT M (0 0 0) * Extended: POINTM(0 0 0) * OGC: POINT(0 0) */ static void lwpoint_to_wkt_sb(const LWPOINT *pt, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "POINT"); /* "POINT" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)pt, sb, variant); } if ( lwpoint_is_empty(pt) ) { empty_to_wkt_sb(sb); return; } ptarray_to_wkt_sb(pt->point, sb, precision, variant); } /* * LINESTRING(0 0 0, 1 1 1) */ static void lwline_to_wkt_sb(const LWLINE *line, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "LINESTRING"); /* "LINESTRING" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)line, sb, variant); } if ( lwline_is_empty(line) ) { empty_to_wkt_sb(sb); return; } ptarray_to_wkt_sb(line->points, sb, precision, variant); } /* * POLYGON(0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1) */ static void lwpoly_to_wkt_sb(const LWPOLY *poly, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "POLYGON"); /* "POLYGON" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)poly, sb, variant); } if ( lwpoly_is_empty(poly) ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); for ( i = 0; i < poly->nrings; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); ptarray_to_wkt_sb(poly->rings[i], sb, precision, variant); } stringbuffer_append(sb, ")"); } /* * CIRCULARSTRING */ static void lwcircstring_to_wkt_sb(const LWCIRCSTRING *circ, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "CIRCULARSTRING"); /* "CIRCULARSTRING" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)circ, sb, variant); } if ( lwcircstring_is_empty(circ) ) { empty_to_wkt_sb(sb); return; } ptarray_to_wkt_sb(circ->points, sb, precision, variant); } /* * Multi-points do not wrap their sub-members in parens, unlike other multi-geometries. * MULTPOINT(0 0, 1 1) instead of MULTIPOINT((0 0),(1 1)) */ static void lwmpoint_to_wkt_sb(const LWMPOINT *mpoint, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "MULTIPOINT"); /* "MULTIPOINT" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoint, sb, variant); } if ( mpoint->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mpoint->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); /* We don't want type strings or parens on our subgeoms */ lwpoint_to_wkt_sb(mpoint->geoms[i], sb, precision, variant | WKT_NO_PARENS | WKT_NO_TYPE ); } stringbuffer_append(sb, ")"); } /* * MULTILINESTRING */ static void lwmline_to_wkt_sb(const LWMLINE *mline, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "MULTILINESTRING"); /* "MULTILINESTRING" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)mline, sb, variant); } if ( mline->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mline->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); /* We don't want type strings on our subgeoms */ lwline_to_wkt_sb(mline->geoms[i], sb, precision, variant | WKT_NO_TYPE ); } stringbuffer_append(sb, ")"); } /* * MULTIPOLYGON */ static void lwmpoly_to_wkt_sb(const LWMPOLY *mpoly, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "MULTIPOLYGON"); /* "MULTIPOLYGON" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoly, sb, variant); } if ( mpoly->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mpoly->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); /* We don't want type strings on our subgeoms */ lwpoly_to_wkt_sb(mpoly->geoms[i], sb, precision, variant | WKT_NO_TYPE ); } stringbuffer_append(sb, ")"); } /* * Compound curves provide type information for their curved sub-geometries * but not their linestring sub-geometries. * COMPOUNDCURVE((0 0, 1 1), CURVESTRING(1 1, 2 2, 3 3)) */ static void lwcompound_to_wkt_sb(const LWCOMPOUND *comp, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "COMPOUNDCURVE"); /* "COMPOUNDCURVE" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)comp, sb, variant); } if ( comp->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < comp->ngeoms; i++ ) { int type = comp->geoms[i]->type; if ( i > 0 ) stringbuffer_append(sb, ","); /* Linestring subgeoms don't get type identifiers */ if ( type == LINETYPE ) { lwline_to_wkt_sb((LWLINE*)comp->geoms[i], sb, precision, variant | WKT_NO_TYPE ); } /* But circstring subgeoms *do* get type identifiers */ else if ( type == CIRCSTRINGTYPE ) { lwcircstring_to_wkt_sb((LWCIRCSTRING*)comp->geoms[i], sb, precision, variant ); } else { lwerror("lwcompound_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type)); } } stringbuffer_append(sb, ")"); } /* * Curve polygons provide type information for their curved rings * but not their linestring rings. * CURVEPOLYGON((0 0, 1 1, 0 1, 0 0), CURVESTRING(0 0, 1 1, 0 1, 0.5 1, 0 0)) */ static void lwcurvepoly_to_wkt_sb(const LWCURVEPOLY *cpoly, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "CURVEPOLYGON"); /* "CURVEPOLYGON" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)cpoly, sb, variant); } if ( cpoly->nrings < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < cpoly->nrings; i++ ) { int type = cpoly->rings[i]->type; if ( i > 0 ) stringbuffer_append(sb, ","); switch (type) { case LINETYPE: /* Linestring subgeoms don't get type identifiers */ lwline_to_wkt_sb((LWLINE*)cpoly->rings[i], sb, precision, variant | WKT_NO_TYPE ); break; case CIRCSTRINGTYPE: /* But circstring subgeoms *do* get type identifiers */ lwcircstring_to_wkt_sb((LWCIRCSTRING*)cpoly->rings[i], sb, precision, variant ); break; case COMPOUNDTYPE: /* And compoundcurve subgeoms *do* get type identifiers */ lwcompound_to_wkt_sb((LWCOMPOUND*)cpoly->rings[i], sb, precision, variant ); break; default: lwerror("lwcurvepoly_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type)); } } stringbuffer_append(sb, ")"); } /* * Multi-curves provide type information for their curved sub-geometries * but not their linear sub-geometries. * MULTICURVE((0 0, 1 1), CURVESTRING(0 0, 1 1, 2 2)) */ static void lwmcurve_to_wkt_sb(const LWMCURVE *mcurv, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "MULTICURVE"); /* "MULTICURVE" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)mcurv, sb, variant); } if ( mcurv->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mcurv->ngeoms; i++ ) { int type = mcurv->geoms[i]->type; if ( i > 0 ) stringbuffer_append(sb, ","); switch (type) { case LINETYPE: /* Linestring subgeoms don't get type identifiers */ lwline_to_wkt_sb((LWLINE*)mcurv->geoms[i], sb, precision, variant | WKT_NO_TYPE ); break; case CIRCSTRINGTYPE: /* But circstring subgeoms *do* get type identifiers */ lwcircstring_to_wkt_sb((LWCIRCSTRING*)mcurv->geoms[i], sb, precision, variant ); break; case COMPOUNDTYPE: /* And compoundcurve subgeoms *do* get type identifiers */ lwcompound_to_wkt_sb((LWCOMPOUND*)mcurv->geoms[i], sb, precision, variant ); break; default: lwerror("lwmcurve_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type)); } } stringbuffer_append(sb, ")"); } /* * Multi-surfaces provide type information for their curved sub-geometries * but not their linear sub-geometries. * MULTISURFACE(((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) */ static void lwmsurface_to_wkt_sb(const LWMSURFACE *msurf, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "MULTISURFACE"); /* "MULTISURFACE" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)msurf, sb, variant); } if ( msurf->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < msurf->ngeoms; i++ ) { int type = msurf->geoms[i]->type; if ( i > 0 ) stringbuffer_append(sb, ","); switch (type) { case POLYGONTYPE: /* Linestring subgeoms don't get type identifiers */ lwpoly_to_wkt_sb((LWPOLY*)msurf->geoms[i], sb, precision, variant | WKT_NO_TYPE ); break; case CURVEPOLYTYPE: /* But circstring subgeoms *do* get type identifiers */ lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)msurf->geoms[i], sb, precision, variant); break; default: lwerror("lwmsurface_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type)); } } stringbuffer_append(sb, ")"); } /* * Geometry collections provide type information for all their curved sub-geometries * but not their linear sub-geometries. * GEOMETRYCOLLECTION(POLYGON((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) */ static void lwcollection_to_wkt_sb(const LWCOLLECTION *collection, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "GEOMETRYCOLLECTION"); /* "GEOMETRYCOLLECTION" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)collection, sb, variant); } if ( collection->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are children */ for ( i = 0; i < collection->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); lwgeom_to_wkt_sb((LWGEOM*)collection->geoms[i], sb, precision, variant ); } stringbuffer_append(sb, ")"); } /* * TRIANGLE */ static void lwtriangle_to_wkt_sb(const LWTRIANGLE *tri, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "TRIANGLE"); /* "TRIANGLE" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)tri, sb, variant); } if ( lwtriangle_is_empty(tri) ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); /* Triangles have extraneous brackets */ ptarray_to_wkt_sb(tri->points, sb, precision, variant); stringbuffer_append(sb, ")"); } /* * TIN */ static void lwtin_to_wkt_sb(const LWTIN *tin, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "TIN"); /* "TIN" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)tin, sb, variant); } if ( tin->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } stringbuffer_append(sb, "("); for ( i = 0; i < tin->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); /* We don't want type strings on our subgeoms */ lwtriangle_to_wkt_sb(tin->geoms[i], sb, precision, variant | WKT_NO_TYPE ); } stringbuffer_append(sb, ")"); } /* * POLYHEDRALSURFACE */ static void lwpsurface_to_wkt_sb(const LWPSURFACE *psurf, stringbuffer_t *sb, int precision, uint8_t variant) { uint32_t i = 0; if ( ! (variant & WKT_NO_TYPE) ) { stringbuffer_append(sb, "POLYHEDRALSURFACE"); /* "POLYHEDRALSURFACE" */ dimension_qualifiers_to_wkt_sb((LWGEOM*)psurf, sb, variant); } if ( psurf->ngeoms < 1 ) { empty_to_wkt_sb(sb); return; } variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */ stringbuffer_append(sb, "("); for ( i = 0; i < psurf->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(sb, ","); /* We don't want type strings on our subgeoms */ lwpoly_to_wkt_sb(psurf->geoms[i], sb, precision, variant | WKT_NO_TYPE ); } stringbuffer_append(sb, ")"); } /* * Generic GEOMETRY */ static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant) { LWDEBUGF(4, "lwgeom_to_wkt_sb: type %s, hasz %d, hasm %d", lwtype_name(geom->type), (geom->type), FLAGS_GET_Z(geom->flags)?1:0, FLAGS_GET_M(geom->flags)?1:0); switch (geom->type) { case POINTTYPE: lwpoint_to_wkt_sb((LWPOINT*)geom, sb, precision, variant); break; case LINETYPE: lwline_to_wkt_sb((LWLINE*)geom, sb, precision, variant); break; case POLYGONTYPE: lwpoly_to_wkt_sb((LWPOLY*)geom, sb, precision, variant); break; case MULTIPOINTTYPE: lwmpoint_to_wkt_sb((LWMPOINT*)geom, sb, precision, variant); break; case MULTILINETYPE: lwmline_to_wkt_sb((LWMLINE*)geom, sb, precision, variant); break; case MULTIPOLYGONTYPE: lwmpoly_to_wkt_sb((LWMPOLY*)geom, sb, precision, variant); break; case COLLECTIONTYPE: lwcollection_to_wkt_sb((LWCOLLECTION*)geom, sb, precision, variant); break; case CIRCSTRINGTYPE: lwcircstring_to_wkt_sb((LWCIRCSTRING*)geom, sb, precision, variant); break; case COMPOUNDTYPE: lwcompound_to_wkt_sb((LWCOMPOUND*)geom, sb, precision, variant); break; case CURVEPOLYTYPE: lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)geom, sb, precision, variant); break; case MULTICURVETYPE: lwmcurve_to_wkt_sb((LWMCURVE*)geom, sb, precision, variant); break; case MULTISURFACETYPE: lwmsurface_to_wkt_sb((LWMSURFACE*)geom, sb, precision, variant); break; case TRIANGLETYPE: lwtriangle_to_wkt_sb((LWTRIANGLE*)geom, sb, precision, variant); break; case TINTYPE: lwtin_to_wkt_sb((LWTIN*)geom, sb, precision, variant); break; case POLYHEDRALSURFACETYPE: lwpsurface_to_wkt_sb((LWPSURFACE*)geom, sb, precision, variant); break; default: lwerror("lwgeom_to_wkt_sb: Type %d - %s unsupported.", geom->type, lwtype_name(geom->type)); } } /** * WKT emitter function. Allocates a new *char and fills it with the WKT * representation. If size_out is not NULL, it will be set to the size of the * allocated *char. * * @param variant Bitmasked value, accepts one of WKT_ISO, WKT_SFSQL, * WKT_EXTENDED. * @param precision Maximal number of digits after comma in the output doubles. * @param size_out If supplied, will return the size of the returned string, * including the null terminator. */ char* lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out) { stringbuffer_t *sb; char *str = NULL; if ( geom == NULL ) return NULL; sb = stringbuffer_create(); /* Extended mode starts with an "SRID=" section for geoms that have one */ if ( (variant & WKT_EXTENDED) && lwgeom_has_srid(geom) ) { stringbuffer_aprintf(sb, "SRID=%d;", geom->srid); } lwgeom_to_wkt_sb(geom, sb, precision, variant); if ( stringbuffer_getstring(sb) == NULL ) { lwerror("Uh oh"); return NULL; } str = stringbuffer_getstringcopy(sb); if ( size_out ) *size_out = stringbuffer_getlength(sb) + 1; stringbuffer_destroy(sb); return str; } lwgeom/src/liblwgeom/lwin_wkt_parse.c0000644000176200001440000027735113773172540017540 0ustar liggesusers/* A Bison parser, made by GNU Bison 3.4. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Undocumented macros, especially those whose name start with YY_, are private implementation details. Do not rely on them. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse wkt_yyparse #define yylex wkt_yylex #define yyerror wkt_yyerror #define yydebug wkt_yydebug #define yynerrs wkt_yynerrs #define yylval wkt_yylval #define yychar wkt_yychar #define yylloc wkt_yylloc /* First part of user prologue. */ #line 1 "lwin_wkt_parse.y" /* WKT Parser */ #include #include #include #include "lwin_wkt.h" #include "lwin_wkt_parse.h" #include "lwgeom_log.h" /* Prototypes to quiet the compiler */ int wkt_yyparse(void); void wkt_yyerror(const char *str); int wkt_yylex(void); /* Declare the global parser variable */ LWGEOM_PARSER_RESULT global_parser_result; /* Turn on/off verbose parsing (turn off for production) */ int wkt_yydebug = 0; /* * Error handler called by the bison parser. Mostly we will be * catching our own errors and filling out the message and errlocation * from WKT_ERROR in the grammar, but we keep this one * around just in case. */ void wkt_yyerror(__attribute__((__unused__)) const char *str) { /* If we haven't already set a message and location, let's set one now. */ if ( ! global_parser_result.message ) { global_parser_result.message = parser_error_messages[PARSER_ERROR_OTHER]; global_parser_result.errcode = PARSER_ERROR_OTHER; global_parser_result.errlocation = wkt_yylloc.last_column; } LWDEBUGF(4,"%s", str); } /** * Parse a WKT geometry string into an LWGEOM structure. Note that this * process uses globals and is not re-entrant, so don't call it within itself * (eg, from within other functions in lwin_wkt.c) or from a threaded program. * Note that parser_result.wkinput picks up a reference to wktstr. */ int lwgeom_parse_wkt(LWGEOM_PARSER_RESULT *parser_result, char *wktstr, int parser_check_flags) { int parse_rv = 0; /* Clean up our global parser result. */ lwgeom_parser_result_init(&global_parser_result); /* Work-around possible bug in GNU Bison 3.0.2 resulting in wkt_yylloc * members not being initialized on yyparse() as documented here: * https://www.gnu.org/software/bison/manual/html_node/Location-Type.html * See discussion here: * http://lists.osgeo.org/pipermail/postgis-devel/2014-September/024506.html */ wkt_yylloc.last_column = wkt_yylloc.last_line = \ wkt_yylloc.first_column = wkt_yylloc.first_line = 1; /* Set the input text string, and parse checks. */ global_parser_result.wkinput = wktstr; global_parser_result.parser_check_flags = parser_check_flags; wkt_lexer_init(wktstr); /* Lexer ready */ parse_rv = wkt_yyparse(); /* Run the parse */ LWDEBUGF(4,"wkt_yyparse returned %d", parse_rv); wkt_lexer_close(); /* Clean up lexer */ /* A non-zero parser return is an error. */ if ( parse_rv || global_parser_result.errcode ) { if( ! global_parser_result.errcode ) { global_parser_result.errcode = PARSER_ERROR_OTHER; global_parser_result.message = parser_error_messages[PARSER_ERROR_OTHER]; global_parser_result.errlocation = wkt_yylloc.last_column; } else if (global_parser_result.geom) { lwgeom_free(global_parser_result.geom); global_parser_result.geom = NULL; } LWDEBUGF(5, "error returned by wkt_yyparse() @ %d: [%d] '%s'", global_parser_result.errlocation, global_parser_result.errcode, global_parser_result.message); /* Copy the global values into the return pointer */ *parser_result = global_parser_result; wkt_yylex_destroy(); return LW_FAILURE; } /* Copy the global value into the return pointer */ *parser_result = global_parser_result; wkt_yylex_destroy(); return LW_SUCCESS; } #define WKT_ERROR() { if ( global_parser_result.errcode != 0 ) { YYERROR; } } #line 187 "lwin_wkt_parse.c" # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* Use api.header.include to #include this header instead of duplicating it here. */ #ifndef YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED # define YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int wkt_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { POINT_TOK = 258, LINESTRING_TOK = 259, POLYGON_TOK = 260, MPOINT_TOK = 261, MLINESTRING_TOK = 262, MPOLYGON_TOK = 263, MSURFACE_TOK = 264, MCURVE_TOK = 265, CURVEPOLYGON_TOK = 266, COMPOUNDCURVE_TOK = 267, CIRCULARSTRING_TOK = 268, COLLECTION_TOK = 269, RBRACKET_TOK = 270, LBRACKET_TOK = 271, COMMA_TOK = 272, EMPTY_TOK = 273, SEMICOLON_TOK = 274, TRIANGLE_TOK = 275, TIN_TOK = 276, POLYHEDRALSURFACE_TOK = 277, DOUBLE_TOK = 278, DIMENSIONALITY_TOK = 279, SRID_TOK = 280 }; #endif /* Tokens. */ #define POINT_TOK 258 #define LINESTRING_TOK 259 #define POLYGON_TOK 260 #define MPOINT_TOK 261 #define MLINESTRING_TOK 262 #define MPOLYGON_TOK 263 #define MSURFACE_TOK 264 #define MCURVE_TOK 265 #define CURVEPOLYGON_TOK 266 #define COMPOUNDCURVE_TOK 267 #define CIRCULARSTRING_TOK 268 #define COLLECTION_TOK 269 #define RBRACKET_TOK 270 #define LBRACKET_TOK 271 #define COMMA_TOK 272 #define EMPTY_TOK 273 #define SEMICOLON_TOK 274 #define TRIANGLE_TOK 275 #define TIN_TOK 276 #define POLYHEDRALSURFACE_TOK 277 #define DOUBLE_TOK 278 #define DIMENSIONALITY_TOK 279 #define SRID_TOK 280 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 112 "lwin_wkt_parse.y" int integervalue; double doublevalue; char *stringvalue; LWGEOM *geometryvalue; POINT coordinatevalue; POINTARRAY *ptarrayvalue; #line 289 "lwin_wkt_parse.c" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE wkt_yylval; extern YYLTYPE wkt_yylloc; int wkt_yyparse (void); #endif /* !YY_WKT_YY_LWIN_WKT_PARSE_H_INCLUDED */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #define YY_ASSERT(E) ((void) (0 && (E))) #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; YYLTYPE yyls_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 80 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 294 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 26 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 40 /* YYNRULES -- Number of rules. */ #define YYNRULES 136 /* YYNSTATES -- Number of states. */ #define YYNSTATES 264 #define YYUNDEFTOK 2 #define YYMAXUTOK 280 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 216, 216, 218, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 239, 241, 243, 245, 249, 251, 255, 257, 259, 261, 265, 267, 269, 271, 273, 275, 279, 281, 283, 285, 289, 291, 293, 295, 299, 301, 303, 305, 309, 311, 315, 317, 321, 323, 325, 327, 331, 333, 337, 340, 342, 344, 346, 350, 352, 356, 357, 358, 359, 362, 364, 368, 370, 374, 377, 380, 382, 384, 386, 390, 392, 394, 396, 398, 400, 404, 406, 408, 410, 414, 416, 418, 420, 422, 424, 426, 428, 432, 434, 436, 438, 442, 444, 448, 450, 452, 454, 458, 460, 462, 464, 468, 470, 474, 476, 480, 482, 484, 486, 490, 494, 496, 498, 500, 504, 506, 510, 512, 514, 518, 520, 522, 524, 528, 530, 534, 536, 538 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 1 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "POINT_TOK", "LINESTRING_TOK", "POLYGON_TOK", "MPOINT_TOK", "MLINESTRING_TOK", "MPOLYGON_TOK", "MSURFACE_TOK", "MCURVE_TOK", "CURVEPOLYGON_TOK", "COMPOUNDCURVE_TOK", "CIRCULARSTRING_TOK", "COLLECTION_TOK", "RBRACKET_TOK", "LBRACKET_TOK", "COMMA_TOK", "EMPTY_TOK", "SEMICOLON_TOK", "TRIANGLE_TOK", "TIN_TOK", "POLYHEDRALSURFACE_TOK", "DOUBLE_TOK", "DIMENSIONALITY_TOK", "SRID_TOK", "$accept", "geometry", "geometry_no_srid", "geometrycollection", "geometry_list", "multisurface", "surface_list", "tin", "polyhedralsurface", "multipolygon", "polygon_list", "patch_list", "polygon", "polygon_untagged", "patch", "curvepolygon", "curvering_list", "curvering", "patchring_list", "ring_list", "patchring", "ring", "compoundcurve", "compound_list", "multicurve", "curve_list", "multilinestring", "linestring_list", "circularstring", "linestring", "linestring_untagged", "triangle_list", "triangle", "triangle_untagged", "multipoint", "point_list", "point_untagged", "point", "ptarray", "coordinate", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280 }; # endif #define YYPACT_NINF -90 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-90))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int16 yypact[] = { 109, -2, 16, 23, 26, 36, 39, 40, 52, 53, 74, 79, 83, 84, 108, 137, 7, 46, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, 43, -90, 27, 43, -90, 88, 33, -90, 144, 140, -90, 167, 175, -90, 176, 179, -90, 183, 20, -90, 184, 11, -90, 187, 11, -90, 188, 17, -90, 191, 43, -90, 192, 168, -90, 195, 51, -90, 196, 56, -90, 199, 70, -90, 200, 168, -90, 68, 110, -90, 43, -90, 169, 43, -90, 43, 204, -90, 33, -90, 43, -90, 205, -90, -90, 140, -90, 43, -90, 208, -90, 175, -90, 33, -90, 209, -90, 179, -90, 212, -90, -90, -90, 20, -90, -90, 213, -90, -90, -90, 11, -90, 216, -90, -90, -90, -90, -90, 11, -90, 217, -90, -90, -90, 17, -90, 220, 43, -90, -90, 221, 168, -90, 43, 80, -90, 93, 224, -90, 56, -90, 94, 225, -90, 70, -90, -90, 105, -90, 43, 228, -90, 229, 232, -90, 33, 233, 44, -90, 140, 236, 237, -90, 175, 240, 241, -90, 179, 244, -90, 20, 245, -90, 11, 248, -90, 11, 249, -90, 17, 252, -90, 253, -90, 168, 256, 257, 43, 43, -90, 56, 260, 43, 261, -90, -90, 70, 264, 112, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, 47, 265, 268, -90, -90, 269, -90, 94, -90, -90, -90, -90, 131, 132, -90, -90, -90, -90 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 18, 13, 15, 16, 12, 8, 9, 7, 14, 11, 6, 5, 17, 10, 4, 0, 131, 0, 0, 109, 0, 0, 54, 0, 0, 122, 0, 0, 99, 0, 0, 46, 0, 0, 28, 0, 0, 87, 0, 0, 61, 0, 0, 77, 0, 0, 105, 0, 0, 22, 0, 0, 117, 0, 0, 38, 0, 0, 42, 0, 0, 1, 0, 0, 133, 0, 130, 0, 0, 108, 0, 0, 71, 0, 53, 0, 127, 0, 124, 125, 0, 121, 0, 111, 0, 101, 0, 98, 0, 56, 0, 48, 0, 45, 0, 32, 34, 33, 0, 27, 93, 0, 92, 94, 95, 0, 86, 0, 63, 66, 67, 65, 64, 0, 60, 0, 81, 82, 83, 0, 76, 0, 0, 104, 24, 0, 0, 21, 0, 0, 116, 0, 0, 113, 0, 37, 0, 0, 50, 0, 41, 3, 134, 128, 0, 0, 106, 0, 0, 51, 0, 0, 0, 119, 0, 0, 0, 96, 0, 0, 0, 43, 0, 0, 25, 0, 0, 84, 0, 0, 58, 0, 0, 74, 0, 0, 102, 0, 19, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 69, 39, 0, 0, 135, 132, 129, 107, 73, 70, 52, 126, 123, 120, 110, 100, 97, 55, 47, 44, 29, 31, 30, 26, 89, 88, 90, 91, 85, 62, 59, 78, 79, 80, 75, 103, 23, 20, 0, 0, 0, 112, 36, 0, 57, 0, 49, 40, 136, 114, 0, 0, 72, 68, 115, 118 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -90, -90, 0, -90, 5, -90, 37, -90, -90, -90, 48, 6, -39, -33, -42, -32, 55, -21, -90, -89, -57, 118, -50, 150, -90, 165, -90, 185, -51, -49, -44, 138, -90, 89, -90, 193, 121, -90, -36, -6 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 17, 143, 19, 144, 20, 113, 21, 22, 23, 109, 156, 24, 110, 157, 25, 126, 127, 207, 90, 208, 91, 26, 134, 27, 120, 28, 103, 29, 30, 131, 151, 31, 152, 32, 96, 97, 33, 82, 83 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint16 yytable[] = { 18, 86, 104, 170, 121, 119, 122, 129, 128, 130, 135, 123, 136, 114, 34, 2, 35, 137, 179, 115, 116, 2, 36, 10, 11, 3, 79, 101, 140, 102, 11, 9, 37, 101, 38, 102, 107, 98, 108, 40, 39, 41, 43, 84, 44, 85, 80, 42, 164, 89, 45, 166, 46, 167, 47, 49, 52, 50, 53, 219, 48, 104, 257, 51, 54, 175, 81, 147, 55, 58, 56, 59, 150, 121, 119, 122, 57, 60, 114, 160, 123, 129, 128, 130, 115, 116, 155, 135, 171, 136, 61, 161, 62, 98, 137, 64, 201, 65, 63, 67, 70, 68, 71, 66, 87, 196, 88, 69, 72, 202, 206, 200, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 73, 162, 74, 163, 212, 13, 14, 15, 75, 223, 16, 256, 233, 232, 234, 129, 128, 130, 239, 235, 240, 228, 262, 263, 226, 241, 199, 229, 230, 76, 185, 77, 94, 213, 95, 182, 92, 78, 93, 81, 211, 247, 248, 98, 254, 237, 251, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99, 165, 100, 163, 191, 13, 14, 15, 101, 105, 102, 106, 107, 261, 108, 244, 111, 117, 112, 118, 124, 132, 125, 133, 138, 141, 139, 142, 145, 148, 146, 149, 153, 158, 154, 159, 168, 172, 169, 173, 176, 180, 177, 181, 183, 186, 184, 187, 189, 192, 190, 193, 195, 197, 163, 198, 203, 209, 204, 210, 214, 215, 163, 163, 216, 218, 163, 169, 221, 222, 173, 163, 224, 225, 177, 169, 227, 231, 181, 184, 236, 238, 187, 190, 242, 243, 193, 163, 245, 246, 198, 163, 250, 252, 204, 253, 255, 258, 210, 163, 259, 260, 163, 163, 217, 194, 188, 178, 205, 174, 249, 220 }; static const yytype_uint8 yycheck[] = { 0, 37, 46, 92, 55, 55, 55, 58, 58, 58, 61, 55, 61, 52, 16, 4, 18, 61, 107, 52, 52, 4, 24, 12, 13, 5, 19, 16, 64, 18, 13, 11, 16, 16, 18, 18, 16, 43, 18, 16, 24, 18, 16, 16, 18, 18, 0, 24, 84, 16, 24, 87, 16, 89, 18, 16, 16, 18, 18, 15, 24, 105, 15, 24, 24, 101, 23, 16, 16, 16, 18, 18, 16, 124, 124, 124, 24, 24, 117, 79, 124, 132, 132, 132, 117, 117, 16, 138, 94, 138, 16, 23, 18, 99, 138, 16, 16, 18, 24, 16, 16, 18, 18, 24, 16, 141, 18, 24, 24, 16, 16, 147, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 15, 18, 17, 23, 20, 21, 22, 24, 177, 25, 23, 187, 187, 187, 190, 190, 190, 193, 187, 193, 184, 15, 15, 181, 193, 145, 184, 184, 16, 117, 18, 16, 163, 18, 111, 16, 24, 18, 23, 158, 201, 202, 173, 210, 190, 206, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 15, 18, 17, 132, 20, 21, 22, 16, 16, 18, 18, 16, 253, 18, 198, 16, 16, 18, 18, 16, 16, 18, 18, 16, 16, 18, 18, 16, 16, 18, 18, 16, 16, 18, 18, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 15, 15, 17, 17, 169, 138, 124, 105, 153, 99, 204, 173 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 21, 22, 25, 27, 28, 29, 31, 33, 34, 35, 38, 41, 48, 50, 52, 54, 55, 58, 60, 63, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 16, 18, 24, 19, 0, 23, 64, 65, 16, 18, 64, 16, 18, 16, 45, 47, 16, 18, 16, 18, 61, 62, 65, 16, 18, 16, 18, 53, 56, 16, 18, 16, 18, 36, 39, 16, 18, 32, 38, 39, 41, 16, 18, 48, 51, 54, 55, 56, 16, 18, 42, 43, 48, 54, 55, 56, 16, 18, 49, 54, 55, 56, 16, 18, 64, 16, 18, 28, 30, 16, 18, 16, 16, 18, 16, 57, 59, 16, 18, 16, 37, 40, 16, 18, 28, 23, 15, 17, 64, 15, 64, 64, 15, 17, 45, 65, 15, 17, 61, 64, 15, 17, 53, 45, 15, 17, 36, 15, 17, 32, 15, 17, 51, 15, 17, 42, 15, 17, 49, 15, 64, 15, 17, 30, 64, 16, 16, 15, 17, 57, 16, 44, 46, 15, 17, 37, 23, 65, 15, 15, 15, 47, 15, 15, 62, 15, 15, 56, 15, 15, 39, 15, 38, 39, 41, 15, 48, 54, 55, 56, 15, 43, 15, 54, 55, 56, 15, 15, 28, 15, 15, 64, 64, 59, 15, 64, 15, 17, 40, 15, 23, 15, 15, 15, 15, 46, 15, 15 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 26, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 38, 38, 38, 38, 39, 39, 40, 41, 41, 41, 41, 42, 42, 43, 43, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, 58, 58, 58, 58, 59, 60, 60, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 63, 64, 64, 65, 65, 65 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 3, 2, 3, 1, 4, 5, 3, 2, 3, 3, 3, 1, 1, 1, 4, 5, 3, 2, 4, 5, 3, 2, 4, 5, 3, 2, 3, 1, 3, 1, 4, 5, 3, 2, 3, 1, 3, 4, 5, 3, 2, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 3, 4, 5, 3, 2, 3, 3, 3, 1, 1, 1, 4, 5, 3, 2, 3, 3, 3, 3, 1, 1, 1, 1, 4, 5, 3, 2, 3, 1, 4, 5, 3, 2, 4, 5, 3, 2, 3, 1, 3, 1, 6, 7, 3, 2, 5, 4, 5, 3, 2, 3, 1, 1, 3, 1, 4, 5, 3, 2, 3, 1, 2, 3, 4 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K]) /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ YY_ATTRIBUTE_UNUSED static int yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) { int res = 0; int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; if (0 <= yylocp->first_line) { res += YYFPRINTF (yyo, "%d", yylocp->first_line); if (0 <= yylocp->first_column) res += YYFPRINTF (yyo, ".%d", yylocp->first_column); } if (0 <= yylocp->last_line) { if (yylocp->first_line < yylocp->last_line) { res += YYFPRINTF (yyo, "-%d", yylocp->last_line); if (0 <= end_col) res += YYFPRINTF (yyo, ".%d", end_col); } else if (0 <= end_col && yylocp->first_column < end_col) res += YYFPRINTF (yyo, "-%d", end_col); } return res; } # define YY_LOCATION_PRINT(File, Loc) \ yy_location_print_ (File, &(Loc)) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*-----------------------------------. | Print this symbol's value on YYO. | `-----------------------------------*/ static void yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) { FILE *yyoutput = yyo; YYUSE (yyoutput); YYUSE (yylocationp); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyo, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*---------------------------. | Print this symbol on YYO. | `---------------------------*/ static void yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) { YYFPRINTF (yyo, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); YY_LOCATION_PRINT (yyo, *yylocationp); YYFPRINTF (yyo, ": "); yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp); YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) { unsigned long yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &yyvsp[(yyi + 1) - (yynrhs)] , &(yylsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, yylsp, Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres); } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return 2; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return 2; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp) { YYUSE (yyvaluep); YYUSE (yylocationp); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 28: /* geometry_no_srid */ #line 194 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1399 "lwin_wkt_parse.c" break; case 29: /* geometrycollection */ #line 195 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1405 "lwin_wkt_parse.c" break; case 31: /* multisurface */ #line 202 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1411 "lwin_wkt_parse.c" break; case 32: /* surface_list */ #line 181 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1417 "lwin_wkt_parse.c" break; case 33: /* tin */ #line 209 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1423 "lwin_wkt_parse.c" break; case 34: /* polyhedralsurface */ #line 208 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1429 "lwin_wkt_parse.c" break; case 35: /* multipolygon */ #line 201 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1435 "lwin_wkt_parse.c" break; case 36: /* polygon_list */ #line 182 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1441 "lwin_wkt_parse.c" break; case 37: /* patch_list */ #line 183 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1447 "lwin_wkt_parse.c" break; case 38: /* polygon */ #line 205 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1453 "lwin_wkt_parse.c" break; case 39: /* polygon_untagged */ #line 207 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1459 "lwin_wkt_parse.c" break; case 40: /* patch */ #line 206 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1465 "lwin_wkt_parse.c" break; case 41: /* curvepolygon */ #line 192 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1471 "lwin_wkt_parse.c" break; case 42: /* curvering_list */ #line 179 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1477 "lwin_wkt_parse.c" break; case 43: /* curvering */ #line 193 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1483 "lwin_wkt_parse.c" break; case 44: /* patchring_list */ #line 189 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1489 "lwin_wkt_parse.c" break; case 45: /* ring_list */ #line 188 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1495 "lwin_wkt_parse.c" break; case 46: /* patchring */ #line 178 "lwin_wkt_parse.y" { ptarray_free(((*yyvaluep).ptarrayvalue)); } #line 1501 "lwin_wkt_parse.c" break; case 47: /* ring */ #line 177 "lwin_wkt_parse.y" { ptarray_free(((*yyvaluep).ptarrayvalue)); } #line 1507 "lwin_wkt_parse.c" break; case 48: /* compoundcurve */ #line 191 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1513 "lwin_wkt_parse.c" break; case 49: /* compound_list */ #line 187 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1519 "lwin_wkt_parse.c" break; case 50: /* multicurve */ #line 198 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1525 "lwin_wkt_parse.c" break; case 51: /* curve_list */ #line 186 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1531 "lwin_wkt_parse.c" break; case 52: /* multilinestring */ #line 199 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1537 "lwin_wkt_parse.c" break; case 53: /* linestring_list */ #line 185 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1543 "lwin_wkt_parse.c" break; case 54: /* circularstring */ #line 190 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1549 "lwin_wkt_parse.c" break; case 55: /* linestring */ #line 196 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1555 "lwin_wkt_parse.c" break; case 56: /* linestring_untagged */ #line 197 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1561 "lwin_wkt_parse.c" break; case 57: /* triangle_list */ #line 180 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1567 "lwin_wkt_parse.c" break; case 58: /* triangle */ #line 210 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1573 "lwin_wkt_parse.c" break; case 59: /* triangle_untagged */ #line 211 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1579 "lwin_wkt_parse.c" break; case 60: /* multipoint */ #line 200 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1585 "lwin_wkt_parse.c" break; case 61: /* point_list */ #line 184 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1591 "lwin_wkt_parse.c" break; case 62: /* point_untagged */ #line 204 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1597 "lwin_wkt_parse.c" break; case 63: /* point */ #line 203 "lwin_wkt_parse.y" { lwgeom_free(((*yyvaluep).geometryvalue)); } #line 1603 "lwin_wkt_parse.c" break; case 64: /* ptarray */ #line 176 "lwin_wkt_parse.y" { ptarray_free(((*yyvaluep).ptarrayvalue)); } #line 1609 "lwin_wkt_parse.c" break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Location data for the lookahead symbol. */ YYLTYPE yylloc # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL = { 1, 1, 1, 1 } # endif ; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ int yyparse (void) { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. 'yyls': related to locations. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[3]; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yylsp = yyls = yylsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ yylsp[0] = yylloc; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; /*--------------------------------------------------------------------. | yynewstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: YYDPRINTF ((stderr, "Entering state %d\n", yystate)); YY_ASSERT (0 <= yystate && yystate < YYNSTATES); *yyssp = (yytype_int16) yystate; if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE goto yyexhaustedlab; #else { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1); # if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; yyls = yyls1; } # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); yyerror_range[1] = yyloc; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 217 "lwin_wkt_parse.y" { wkt_parser_geometry_new((yyvsp[0].geometryvalue), SRID_UNKNOWN); WKT_ERROR(); } #line 1901 "lwin_wkt_parse.c" break; case 3: #line 219 "lwin_wkt_parse.y" { wkt_parser_geometry_new((yyvsp[0].geometryvalue), (yyvsp[-2].integervalue)); WKT_ERROR(); } #line 1907 "lwin_wkt_parse.c" break; case 4: #line 222 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1913 "lwin_wkt_parse.c" break; case 5: #line 223 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1919 "lwin_wkt_parse.c" break; case 6: #line 224 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1925 "lwin_wkt_parse.c" break; case 7: #line 225 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1931 "lwin_wkt_parse.c" break; case 8: #line 226 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1937 "lwin_wkt_parse.c" break; case 9: #line 227 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1943 "lwin_wkt_parse.c" break; case 10: #line 228 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1949 "lwin_wkt_parse.c" break; case 11: #line 229 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1955 "lwin_wkt_parse.c" break; case 12: #line 230 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1961 "lwin_wkt_parse.c" break; case 13: #line 231 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1967 "lwin_wkt_parse.c" break; case 14: #line 232 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1973 "lwin_wkt_parse.c" break; case 15: #line 233 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1979 "lwin_wkt_parse.c" break; case 16: #line 234 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1985 "lwin_wkt_parse.c" break; case 17: #line 235 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1991 "lwin_wkt_parse.c" break; case 18: #line 236 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 1997 "lwin_wkt_parse.c" break; case 19: #line 240 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COLLECTIONTYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2003 "lwin_wkt_parse.c" break; case 20: #line 242 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COLLECTIONTYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2009 "lwin_wkt_parse.c" break; case 21: #line 244 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COLLECTIONTYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2015 "lwin_wkt_parse.c" break; case 22: #line 246 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COLLECTIONTYPE, NULL, NULL); WKT_ERROR(); } #line 2021 "lwin_wkt_parse.c" break; case 23: #line 250 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2027 "lwin_wkt_parse.c" break; case 24: #line 252 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2033 "lwin_wkt_parse.c" break; case 25: #line 256 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTISURFACETYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2039 "lwin_wkt_parse.c" break; case 26: #line 258 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTISURFACETYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2045 "lwin_wkt_parse.c" break; case 27: #line 260 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTISURFACETYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2051 "lwin_wkt_parse.c" break; case 28: #line 262 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTISURFACETYPE, NULL, NULL); WKT_ERROR(); } #line 2057 "lwin_wkt_parse.c" break; case 29: #line 266 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2063 "lwin_wkt_parse.c" break; case 30: #line 268 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2069 "lwin_wkt_parse.c" break; case 31: #line 270 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2075 "lwin_wkt_parse.c" break; case 32: #line 272 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2081 "lwin_wkt_parse.c" break; case 33: #line 274 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2087 "lwin_wkt_parse.c" break; case 34: #line 276 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2093 "lwin_wkt_parse.c" break; case 35: #line 280 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(TINTYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2099 "lwin_wkt_parse.c" break; case 36: #line 282 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(TINTYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2105 "lwin_wkt_parse.c" break; case 37: #line 284 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(TINTYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2111 "lwin_wkt_parse.c" break; case 38: #line 286 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(TINTYPE, NULL, NULL); WKT_ERROR(); } #line 2117 "lwin_wkt_parse.c" break; case 39: #line 290 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2123 "lwin_wkt_parse.c" break; case 40: #line 292 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2129 "lwin_wkt_parse.c" break; case 41: #line 294 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2135 "lwin_wkt_parse.c" break; case 42: #line 296 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, NULL, NULL); WKT_ERROR(); } #line 2141 "lwin_wkt_parse.c" break; case 43: #line 300 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2147 "lwin_wkt_parse.c" break; case 44: #line 302 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2153 "lwin_wkt_parse.c" break; case 45: #line 304 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2159 "lwin_wkt_parse.c" break; case 46: #line 306 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, NULL, NULL); WKT_ERROR(); } #line 2165 "lwin_wkt_parse.c" break; case 47: #line 310 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2171 "lwin_wkt_parse.c" break; case 48: #line 312 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2177 "lwin_wkt_parse.c" break; case 49: #line 316 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2183 "lwin_wkt_parse.c" break; case 50: #line 318 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2189 "lwin_wkt_parse.c" break; case 51: #line 322 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_finalize((yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2195 "lwin_wkt_parse.c" break; case 52: #line 324 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_finalize((yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2201 "lwin_wkt_parse.c" break; case 53: #line 326 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_finalize(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2207 "lwin_wkt_parse.c" break; case 54: #line 328 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_finalize(NULL, NULL); WKT_ERROR(); } #line 2213 "lwin_wkt_parse.c" break; case 55: #line 332 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[-1].geometryvalue); } #line 2219 "lwin_wkt_parse.c" break; case 56: #line 334 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_finalize(NULL, NULL); WKT_ERROR(); } #line 2225 "lwin_wkt_parse.c" break; case 57: #line 337 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[-1].geometryvalue); } #line 2231 "lwin_wkt_parse.c" break; case 58: #line 341 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_finalize((yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2237 "lwin_wkt_parse.c" break; case 59: #line 343 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_finalize((yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2243 "lwin_wkt_parse.c" break; case 60: #line 345 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_finalize(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2249 "lwin_wkt_parse.c" break; case 61: #line 347 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_finalize(NULL, NULL); WKT_ERROR(); } #line 2255 "lwin_wkt_parse.c" break; case 62: #line 351 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_add_ring((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2261 "lwin_wkt_parse.c" break; case 63: #line 353 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_curvepolygon_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2267 "lwin_wkt_parse.c" break; case 64: #line 356 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 2273 "lwin_wkt_parse.c" break; case 65: #line 357 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 2279 "lwin_wkt_parse.c" break; case 66: #line 358 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 2285 "lwin_wkt_parse.c" break; case 67: #line 359 "lwin_wkt_parse.y" { (yyval.geometryvalue) = (yyvsp[0].geometryvalue); } #line 2291 "lwin_wkt_parse.c" break; case 68: #line 363 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_add_ring((yyvsp[-2].geometryvalue),(yyvsp[0].ptarrayvalue),'Z'); WKT_ERROR(); } #line 2297 "lwin_wkt_parse.c" break; case 69: #line 365 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_new((yyvsp[0].ptarrayvalue),'Z'); WKT_ERROR(); } #line 2303 "lwin_wkt_parse.c" break; case 70: #line 369 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_add_ring((yyvsp[-2].geometryvalue),(yyvsp[0].ptarrayvalue),'2'); WKT_ERROR(); } #line 2309 "lwin_wkt_parse.c" break; case 71: #line 371 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_polygon_new((yyvsp[0].ptarrayvalue),'2'); WKT_ERROR(); } #line 2315 "lwin_wkt_parse.c" break; case 72: #line 374 "lwin_wkt_parse.y" { (yyval.ptarrayvalue) = (yyvsp[-1].ptarrayvalue); } #line 2321 "lwin_wkt_parse.c" break; case 73: #line 377 "lwin_wkt_parse.y" { (yyval.ptarrayvalue) = (yyvsp[-1].ptarrayvalue); } #line 2327 "lwin_wkt_parse.c" break; case 74: #line 381 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COMPOUNDTYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2333 "lwin_wkt_parse.c" break; case 75: #line 383 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COMPOUNDTYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2339 "lwin_wkt_parse.c" break; case 76: #line 385 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COMPOUNDTYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2345 "lwin_wkt_parse.c" break; case 77: #line 387 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(COMPOUNDTYPE, NULL, NULL); WKT_ERROR(); } #line 2351 "lwin_wkt_parse.c" break; case 78: #line 391 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2357 "lwin_wkt_parse.c" break; case 79: #line 393 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2363 "lwin_wkt_parse.c" break; case 80: #line 395 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2369 "lwin_wkt_parse.c" break; case 81: #line 397 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2375 "lwin_wkt_parse.c" break; case 82: #line 399 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2381 "lwin_wkt_parse.c" break; case 83: #line 401 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_compound_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2387 "lwin_wkt_parse.c" break; case 84: #line 405 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTICURVETYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2393 "lwin_wkt_parse.c" break; case 85: #line 407 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTICURVETYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2399 "lwin_wkt_parse.c" break; case 86: #line 409 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTICURVETYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2405 "lwin_wkt_parse.c" break; case 87: #line 411 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTICURVETYPE, NULL, NULL); WKT_ERROR(); } #line 2411 "lwin_wkt_parse.c" break; case 88: #line 415 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2417 "lwin_wkt_parse.c" break; case 89: #line 417 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2423 "lwin_wkt_parse.c" break; case 90: #line 419 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2429 "lwin_wkt_parse.c" break; case 91: #line 421 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2435 "lwin_wkt_parse.c" break; case 92: #line 423 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2441 "lwin_wkt_parse.c" break; case 93: #line 425 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2447 "lwin_wkt_parse.c" break; case 94: #line 427 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2453 "lwin_wkt_parse.c" break; case 95: #line 429 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2459 "lwin_wkt_parse.c" break; case 96: #line 433 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTILINETYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2465 "lwin_wkt_parse.c" break; case 97: #line 435 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTILINETYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2471 "lwin_wkt_parse.c" break; case 98: #line 437 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTILINETYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2477 "lwin_wkt_parse.c" break; case 99: #line 439 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTILINETYPE, NULL, NULL); WKT_ERROR(); } #line 2483 "lwin_wkt_parse.c" break; case 100: #line 443 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2489 "lwin_wkt_parse.c" break; case 101: #line 445 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2495 "lwin_wkt_parse.c" break; case 102: #line 449 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_circularstring_new((yyvsp[-1].ptarrayvalue), NULL); WKT_ERROR(); } #line 2501 "lwin_wkt_parse.c" break; case 103: #line 451 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_circularstring_new((yyvsp[-1].ptarrayvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2507 "lwin_wkt_parse.c" break; case 104: #line 453 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_circularstring_new(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2513 "lwin_wkt_parse.c" break; case 105: #line 455 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_circularstring_new(NULL, NULL); WKT_ERROR(); } #line 2519 "lwin_wkt_parse.c" break; case 106: #line 459 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new((yyvsp[-1].ptarrayvalue), NULL); WKT_ERROR(); } #line 2525 "lwin_wkt_parse.c" break; case 107: #line 461 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new((yyvsp[-1].ptarrayvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2531 "lwin_wkt_parse.c" break; case 108: #line 463 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2537 "lwin_wkt_parse.c" break; case 109: #line 465 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new(NULL, NULL); WKT_ERROR(); } #line 2543 "lwin_wkt_parse.c" break; case 110: #line 469 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new((yyvsp[-1].ptarrayvalue), NULL); WKT_ERROR(); } #line 2549 "lwin_wkt_parse.c" break; case 111: #line 471 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_linestring_new(NULL, NULL); WKT_ERROR(); } #line 2555 "lwin_wkt_parse.c" break; case 112: #line 475 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2561 "lwin_wkt_parse.c" break; case 113: #line 477 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2567 "lwin_wkt_parse.c" break; case 114: #line 481 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_triangle_new((yyvsp[-2].ptarrayvalue), NULL); WKT_ERROR(); } #line 2573 "lwin_wkt_parse.c" break; case 115: #line 483 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_triangle_new((yyvsp[-2].ptarrayvalue), (yyvsp[-5].stringvalue)); WKT_ERROR(); } #line 2579 "lwin_wkt_parse.c" break; case 116: #line 485 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_triangle_new(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2585 "lwin_wkt_parse.c" break; case 117: #line 487 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_triangle_new(NULL, NULL); WKT_ERROR(); } #line 2591 "lwin_wkt_parse.c" break; case 118: #line 491 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_triangle_new((yyvsp[-2].ptarrayvalue), NULL); WKT_ERROR(); } #line 2597 "lwin_wkt_parse.c" break; case 119: #line 495 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOINTTYPE, (yyvsp[-1].geometryvalue), NULL); WKT_ERROR(); } #line 2603 "lwin_wkt_parse.c" break; case 120: #line 497 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOINTTYPE, (yyvsp[-1].geometryvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2609 "lwin_wkt_parse.c" break; case 121: #line 499 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOINTTYPE, NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2615 "lwin_wkt_parse.c" break; case 122: #line 501 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_finalize(MULTIPOINTTYPE, NULL, NULL); WKT_ERROR(); } #line 2621 "lwin_wkt_parse.c" break; case 123: #line 505 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_add_geom((yyvsp[-2].geometryvalue),(yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2627 "lwin_wkt_parse.c" break; case 124: #line 507 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_collection_new((yyvsp[0].geometryvalue)); WKT_ERROR(); } #line 2633 "lwin_wkt_parse.c" break; case 125: #line 511 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new(wkt_parser_ptarray_new((yyvsp[0].coordinatevalue)),NULL); WKT_ERROR(); } #line 2639 "lwin_wkt_parse.c" break; case 126: #line 513 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new(wkt_parser_ptarray_new((yyvsp[-1].coordinatevalue)),NULL); WKT_ERROR(); } #line 2645 "lwin_wkt_parse.c" break; case 127: #line 515 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new(NULL, NULL); WKT_ERROR(); } #line 2651 "lwin_wkt_parse.c" break; case 128: #line 519 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new((yyvsp[-1].ptarrayvalue), NULL); WKT_ERROR(); } #line 2657 "lwin_wkt_parse.c" break; case 129: #line 521 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new((yyvsp[-1].ptarrayvalue), (yyvsp[-3].stringvalue)); WKT_ERROR(); } #line 2663 "lwin_wkt_parse.c" break; case 130: #line 523 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new(NULL, (yyvsp[-1].stringvalue)); WKT_ERROR(); } #line 2669 "lwin_wkt_parse.c" break; case 131: #line 525 "lwin_wkt_parse.y" { (yyval.geometryvalue) = wkt_parser_point_new(NULL,NULL); WKT_ERROR(); } #line 2675 "lwin_wkt_parse.c" break; case 132: #line 529 "lwin_wkt_parse.y" { (yyval.ptarrayvalue) = wkt_parser_ptarray_add_coord((yyvsp[-2].ptarrayvalue), (yyvsp[0].coordinatevalue)); WKT_ERROR(); } #line 2681 "lwin_wkt_parse.c" break; case 133: #line 531 "lwin_wkt_parse.y" { (yyval.ptarrayvalue) = wkt_parser_ptarray_new((yyvsp[0].coordinatevalue)); WKT_ERROR(); } #line 2687 "lwin_wkt_parse.c" break; case 134: #line 535 "lwin_wkt_parse.y" { (yyval.coordinatevalue) = wkt_parser_coord_2((yyvsp[-1].doublevalue), (yyvsp[0].doublevalue)); WKT_ERROR(); } #line 2693 "lwin_wkt_parse.c" break; case 135: #line 537 "lwin_wkt_parse.y" { (yyval.coordinatevalue) = wkt_parser_coord_3((yyvsp[-2].doublevalue), (yyvsp[-1].doublevalue), (yyvsp[0].doublevalue)); WKT_ERROR(); } #line 2699 "lwin_wkt_parse.c" break; case 136: #line 539 "lwin_wkt_parse.y" { (yyval.coordinatevalue) = wkt_parser_coord_4((yyvsp[-3].doublevalue), (yyvsp[-2].doublevalue), (yyvsp[-1].doublevalue), (yyvsp[0].doublevalue)); WKT_ERROR(); } #line 2705 "lwin_wkt_parse.c" break; #line 2709 "lwin_wkt_parse.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *yyssp; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } yyerror_range[1] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[1] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END yyerror_range[2] = yylloc; /* Using YYLLOC is tempting, but would change the location of the lookahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, yyerror_range, 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif /*-----------------------------------------------------. | yyreturn -- parsing is finished, return the result. | `-----------------------------------------------------*/ yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 541 "lwin_wkt_parse.y" lwgeom/src/liblwgeom/lwgeom_geos_split.c0000644000176200001440000003606713773172540020227 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011-2015 Sandro Santilli * **********************************************************************/ #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "lwgeom_geos.h" #include "liblwgeom_internal.h" #include #include static LWGEOM* lwline_split_by_line(const LWLINE* lwgeom_in, const LWGEOM* blade_in); static LWGEOM* lwline_split_by_point(const LWLINE* lwgeom_in, const LWPOINT* blade_in); static LWGEOM* lwline_split_by_mpoint(const LWLINE* lwgeom_in, const LWMPOINT* blade_in); static LWGEOM* lwline_split(const LWLINE* lwgeom_in, const LWGEOM* blade_in); static LWGEOM* lwpoly_split_by_line(const LWPOLY* lwgeom_in, const LWGEOM* blade_in); static LWGEOM* lwcollection_split(const LWCOLLECTION* lwcoll_in, const LWGEOM* blade_in); static LWGEOM* lwpoly_split(const LWPOLY* lwpoly_in, const LWGEOM* blade_in); /* Initializes and uses GEOS internally */ static LWGEOM* lwline_split_by_line(const LWLINE* lwline_in, const LWGEOM* blade_in) { LWGEOM** components; LWGEOM* diff; LWCOLLECTION* out; GEOSGeometry* gdiff; /* difference */ GEOSGeometry* g1; GEOSGeometry* g2; int ret; /* ASSERT blade_in is LINE or MULTILINE */ assert (blade_in->type == LINETYPE || blade_in->type == MULTILINETYPE || blade_in->type == POLYGONTYPE || blade_in->type == MULTIPOLYGONTYPE ); /* Possible outcomes: * * 1. The lines do not cross or overlap * -> Return a collection with single element * 2. The lines cross * -> Return a collection of all elements resulting from the split */ initGEOS(lwgeom_geos_error, lwgeom_geos_error); g1 = LWGEOM2GEOS((LWGEOM*)lwline_in, 0); if ( ! g1 ) { lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } g2 = LWGEOM2GEOS(blade_in, 0); if ( ! g2 ) { GEOSGeom_destroy(g1); lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } /* If blade is a polygon, pick its boundary */ if ( blade_in->type == POLYGONTYPE || blade_in->type == MULTIPOLYGONTYPE ) { gdiff = GEOSBoundary(g2); GEOSGeom_destroy(g2); if ( ! gdiff ) { GEOSGeom_destroy(g1); lwerror("GEOSBoundary: %s", lwgeom_geos_errmsg); return NULL; } g2 = gdiff; gdiff = NULL; } /* If interior intersecton is linear we can't split */ ret = GEOSRelatePattern(g1, g2, "1********"); if ( 2 == ret ) { lwerror("GEOSRelatePattern: %s", lwgeom_geos_errmsg); GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); return NULL; } if ( ret ) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); lwerror("Splitter line has linear intersection with input"); return NULL; } gdiff = GEOSDifference(g1,g2); GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); if (gdiff == NULL) { lwerror("GEOSDifference: %s", lwgeom_geos_errmsg); return NULL; } diff = GEOS2LWGEOM(gdiff, FLAGS_GET_Z(lwline_in->flags)); GEOSGeom_destroy(gdiff); if (NULL == diff) { lwerror("GEOS2LWGEOM: %s", lwgeom_geos_errmsg); return NULL; } out = lwgeom_as_lwcollection(diff); if ( ! out ) { components = lwalloc(sizeof(LWGEOM*)*1); components[0] = diff; out = lwcollection_construct(COLLECTIONTYPE, lwline_in->srid, NULL, 1, components); } else { /* Set SRID */ lwgeom_set_srid((LWGEOM*)out, lwline_in->srid); /* Force collection type */ out->type = COLLECTIONTYPE; } return (LWGEOM*)out; } static LWGEOM* lwline_split_by_point(const LWLINE* lwline_in, const LWPOINT* blade_in) { LWMLINE* out; out = lwmline_construct_empty(lwline_in->srid, FLAGS_GET_Z(lwline_in->flags), FLAGS_GET_M(lwline_in->flags)); if ( lwline_split_by_point_to(lwline_in, blade_in, out) < 2 ) { lwmline_add_lwline(out, lwline_clone_deep(lwline_in)); } /* Turn multiline into collection */ out->type = COLLECTIONTYPE; return (LWGEOM*)out; } static LWGEOM* lwline_split_by_mpoint(const LWLINE* lwline_in, const LWMPOINT* mp) { LWMLINE* out; uint32_t i, j; out = lwmline_construct_empty(lwline_in->srid, FLAGS_GET_Z(lwline_in->flags), FLAGS_GET_M(lwline_in->flags)); lwmline_add_lwline(out, lwline_clone_deep(lwline_in)); for (i=0; ingeoms; ++i) { for (j=0; jngeoms; ++j) { lwline_in = out->geoms[j]; LWPOINT *blade_in = mp->geoms[i]; int ret = lwline_split_by_point_to(lwline_in, blade_in, out); if ( 2 == ret ) { /* the point splits this line, * 2 splits were added to collection. * We'll move the latest added into * the slot of the current one. */ lwline_free(out->geoms[j]); out->geoms[j] = out->geoms[--out->ngeoms]; } } } /* Turn multiline into collection */ out->type = COLLECTIONTYPE; return (LWGEOM*)out; } int lwline_split_by_point_to(const LWLINE* lwline_in, const LWPOINT* blade_in, LWMLINE* v) { double mindist_sqr = -1; POINT4D pt, pt_projected; POINT4D p1, p2; POINTARRAY *ipa = lwline_in->points; POINTARRAY* pa1; POINTARRAY* pa2; uint32_t i, nsegs, seg = UINT32_MAX; /* Possible outcomes: * * 1. The point is not on the line or on the boundary * -> Leave collection untouched, return 0 * 2. The point is on the boundary * -> Leave collection untouched, return 1 * 3. The point is in the line * -> Push 2 elements on the collection: * o start_point - cut_point * o cut_point - last_point * -> Return 2 */ getPoint4d_p(blade_in->point, 0, &pt); /* Find closest segment */ getPoint4d_p(ipa, 0, &p1); nsegs = ipa->npoints - 1; for ( i = 0; i < nsegs; i++ ) { getPoint4d_p(ipa, i+1, &p2); double dist_sqr = distance2d_sqr_pt_seg((POINT2D *)&pt, (POINT2D *)&p1, (POINT2D *)&p2); LWDEBUGF(4, "Distance (squared) of point %g %g to segment %g %g, %g %g: %g", pt.x, pt.y, p1.x, p1.y, p2.x, p2.y, dist_sqr); if (i == 0 || dist_sqr < mindist_sqr) { mindist_sqr = dist_sqr; seg=i; if (mindist_sqr == 0.0) break; /* can't be closer than ON line */ } p1 = p2; } LWDEBUGF(3, "Closest segment: %d", seg); LWDEBUGF(3, "mindist: %g", mindist_sqr); /* No intersection */ if (mindist_sqr > 0) return 0; /* empty or single-point line, intersection on boundary */ if ( seg == UINT32_MAX ) return 1; /* * We need to project the * point on the closest segment, * to interpolate Z and M if needed */ getPoint4d_p(ipa, seg, &p1); getPoint4d_p(ipa, seg+1, &p2); closest_point_on_segment(&pt, &p1, &p2, &pt_projected); /* But X and Y we want the ones of the input point, * as on some architectures the interpolation math moves the * coordinates (see #3422) */ pt_projected.x = pt.x; pt_projected.y = pt.y; LWDEBUGF(3, "Projected point:(%g %g), seg:%d, p1:(%g %g), p2:(%g %g)", pt_projected.x, pt_projected.y, seg, p1.x, p1.y, p2.x, p2.y); /* When closest point == an endpoint, this is a boundary intersection */ if ( ( (seg == nsegs-1) && p4d_same(&pt_projected, &p2) ) || ( (seg == 0) && p4d_same(&pt_projected, &p1) ) ) { return 1; } /* This is an internal intersection, let's build the two new pointarrays */ pa1 = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), seg+2); /* TODO: replace with a memcpy ? */ for (i=0; i<=seg; ++i) { getPoint4d_p(ipa, i, &p1); ptarray_append_point(pa1, &p1, LW_FALSE); } ptarray_append_point(pa1, &pt_projected, LW_FALSE); pa2 = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), ipa->npoints-seg); ptarray_append_point(pa2, &pt_projected, LW_FALSE); /* TODO: replace with a memcpy (if so need to check for duplicated point) ? */ for (i=seg+1; inpoints; ++i) { getPoint4d_p(ipa, i, &p1); ptarray_append_point(pa2, &p1, LW_FALSE); } /* NOTE: I've seen empty pointarrays with loc != 0 and loc != 1 */ if ( pa1->npoints == 0 || pa2->npoints == 0 ) { ptarray_free(pa1); ptarray_free(pa2); /* Intersection is on the boundary */ return 1; } lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa1)); lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa2)); return 2; } static LWGEOM* lwline_split(const LWLINE* lwline_in, const LWGEOM* blade_in) { switch (blade_in->type) { case POINTTYPE: return lwline_split_by_point(lwline_in, (LWPOINT*)blade_in); case MULTIPOINTTYPE: return lwline_split_by_mpoint(lwline_in, (LWMPOINT*)blade_in); case LINETYPE: case MULTILINETYPE: case POLYGONTYPE: case MULTIPOLYGONTYPE: return lwline_split_by_line(lwline_in, blade_in); default: lwerror("Splitting a Line by a %s is unsupported", lwtype_name(blade_in->type)); return NULL; } return NULL; } /* Initializes and uses GEOS internally */ static LWGEOM* lwpoly_split_by_line(const LWPOLY* lwpoly_in, const LWGEOM* blade_in) { LWCOLLECTION* out; GEOSGeometry* g1; GEOSGeometry* g2; GEOSGeometry* g1_bounds; GEOSGeometry* polygons; const GEOSGeometry *vgeoms[1]; int i,n; int hasZ = FLAGS_GET_Z(lwpoly_in->flags); /* Possible outcomes: * * 1. The line does not split the polygon * -> Return a collection with single element * 2. The line does split the polygon * -> Return a collection of all elements resulting from the split */ initGEOS(lwgeom_geos_error, lwgeom_geos_error); g1 = LWGEOM2GEOS((LWGEOM*)lwpoly_in, 0); if ( NULL == g1 ) { lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } g1_bounds = GEOSBoundary(g1); if ( NULL == g1_bounds ) { GEOSGeom_destroy(g1); lwerror("GEOSBoundary: %s", lwgeom_geos_errmsg); return NULL; } g2 = LWGEOM2GEOS(blade_in, 0); if ( NULL == g2 ) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g1_bounds); lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } vgeoms[0] = GEOSUnion(g1_bounds, g2); if ( NULL == vgeoms[0] ) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g1_bounds); lwerror("GEOSUnion: %s", lwgeom_geos_errmsg); return NULL; } polygons = GEOSPolygonize(vgeoms, 1); if ( NULL == polygons ) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g1_bounds); GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]); lwerror("GEOSPolygonize: %s", lwgeom_geos_errmsg); return NULL; } #if PARANOIA_LEVEL > 0 if ( GEOSGeomTypeId(polygons) != COLLECTIONTYPE ) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g1_bounds); GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy(polygons); lwerror("%s [%s] Unexpected return from GEOSpolygonize", __FILE__, __LINE__); return 0; } #endif /* We should now have all polygons, just skip * the ones which are in holes of the original * geometries and return the rest in a collection */ n = GEOSGetNumGeometries(polygons); out = lwcollection_construct_empty(COLLECTIONTYPE, lwpoly_in->srid, hasZ, 0); /* Allocate space for all polys */ out->geoms = lwrealloc(out->geoms, sizeof(LWGEOM*)*n); assert(0 == out->ngeoms); for (i=0; igeoms[out->ngeoms++] = GEOS2LWGEOM(p, hasZ); } GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g1_bounds); GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy(polygons); return (LWGEOM*)out; } static LWGEOM* lwcollection_split(const LWCOLLECTION* lwcoll_in, const LWGEOM* blade_in) { LWGEOM** split_vector=NULL; LWCOLLECTION* out; size_t split_vector_capacity; size_t split_vector_size=0; size_t i,j; split_vector_capacity=8; split_vector = lwalloc(split_vector_capacity * sizeof(LWGEOM*)); if ( ! split_vector ) { lwerror("Out of virtual memory"); return NULL; } for (i=0; ingeoms; ++i) { LWCOLLECTION* col; LWGEOM* split = lwgeom_split(lwcoll_in->geoms[i], blade_in); /* an exception should prevent this from ever returning NULL */ if ( ! split ) return NULL; col = lwgeom_as_lwcollection(split); /* Output, if any, will always be a collection */ assert(col); /* Reallocate split_vector if needed */ if ( split_vector_size + col->ngeoms > split_vector_capacity ) { /* NOTE: we could be smarter on reallocations here */ split_vector_capacity += col->ngeoms; split_vector = lwrealloc(split_vector, split_vector_capacity * sizeof(LWGEOM*)); if ( ! split_vector ) { lwerror("Out of virtual memory"); return NULL; } } for (j=0; jngeoms; ++j) { col->geoms[j]->srid = SRID_UNKNOWN; /* strip srid */ split_vector[split_vector_size++] = col->geoms[j]; } lwfree(col->geoms); lwfree(col); } /* Now split_vector has split_vector_size geometries */ out = lwcollection_construct(COLLECTIONTYPE, lwcoll_in->srid, NULL, split_vector_size, split_vector); return (LWGEOM*)out; } static LWGEOM* lwpoly_split(const LWPOLY* lwpoly_in, const LWGEOM* blade_in) { switch (blade_in->type) { case MULTILINETYPE: case LINETYPE: return lwpoly_split_by_line(lwpoly_in, blade_in); default: lwerror("Splitting a Polygon by a %s is unsupported", lwtype_name(blade_in->type)); return NULL; } return NULL; } /* exported */ LWGEOM* lwgeom_split(const LWGEOM* lwgeom_in, const LWGEOM* blade_in) { switch (lwgeom_in->type) { case LINETYPE: return lwline_split((const LWLINE*)lwgeom_in, blade_in); case POLYGONTYPE: return lwpoly_split((const LWPOLY*)lwgeom_in, blade_in); case MULTIPOLYGONTYPE: case MULTILINETYPE: case COLLECTIONTYPE: return lwcollection_split((const LWCOLLECTION*)lwgeom_in, blade_in); default: lwerror("Splitting of %s geometries is unsupported", lwtype_name(lwgeom_in->type)); return NULL; } } lwgeom/src/liblwgeom/liblwgeom_topo_internal.h0000644000176200001440000000645513773172540021426 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2015 Sandro Santilli * **********************************************************************/ #ifndef LIBLWGEOM_TOPO_INTERNAL_H #define LIBLWGEOM_TOPO_INTERNAL_H 1 #include "../postgis_config.h" #include "liblwgeom.h" #include "liblwgeom_topo.h" /************************************************************************ * * Generic SQL handler * ************************************************************************/ struct LWT_BE_IFACE_T { const LWT_BE_DATA *data; const LWT_BE_CALLBACKS *cb; }; const char* lwt_be_lastErrorMessage(const LWT_BE_IFACE* be); LWT_BE_TOPOLOGY * lwt_be_loadTopologyByName(LWT_BE_IFACE *be, const char *name); int lwt_be_freeTopology(LWT_TOPOLOGY *topo); LWT_ISO_NODE *lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit); LWT_ISO_NODE *lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); int lwt_be_ExistsCoincidentNode(LWT_TOPOLOGY* topo, LWPOINT* pt); int lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems); int lwt_be_ExistsEdgeIntersectingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt); LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY* topo); LWT_ISO_EDGE *lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); LWT_ISO_EDGE *lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit); int lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems); int lwt_be_updateEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields, const LWT_ISO_EDGE* upd_edge, int upd_fields, const LWT_ISO_EDGE* exc_edge, int exc_fields); int lwt_be_deleteEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields); LWT_ELEMID lwt_be_getFaceContainingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt); int lwt_be_updateTopoGeomEdgeSplit(LWT_TOPOLOGY* topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2); /************************************************************************ * * Internal objects * ************************************************************************/ struct LWT_TOPOLOGY_T { const LWT_BE_IFACE *be_iface; LWT_BE_TOPOLOGY *be_topo; int32_t srid; double precision; int hasZ; }; #endif /* LIBLWGEOM_TOPO_INTERNAL_H */ lwgeom/src/liblwgeom/lwlinearreferencing.c0000644000176200001440000011404213773172540020520 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2015 Sandro Santilli * Copyright (C) 2011 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "measures3d.h" static int segment_locate_along(const POINT4D *p1, const POINT4D *p2, double m, double offset, POINT4D *pn) { double m1 = p1->m; double m2 = p2->m; double mprop; /* M is out of range, no new point generated. */ if ((m < FP_MIN(m1, m2)) || (m > FP_MAX(m1, m2))) { return LW_FALSE; } if (m1 == m2) { /* Degenerate case: same M on both points. If they are the same point we just return one of them. */ if (p4d_same(p1, p2)) { *pn = *p1; return LW_TRUE; } /* If the points are different we split the difference */ mprop = 0.5; } else { mprop = (m - m1) / (m2 - m1); } /* M is in range, new point to be generated. */ pn->x = p1->x + (p2->x - p1->x) * mprop; pn->y = p1->y + (p2->y - p1->y) * mprop; pn->z = p1->z + (p2->z - p1->z) * mprop; pn->m = m; /* Offset to the left or right, if necessary. */ if (offset != 0.0) { double theta = atan2(p2->y - p1->y, p2->x - p1->x); pn->x -= sin(theta) * offset; pn->y += cos(theta) * offset; } return LW_TRUE; } static POINTARRAY * ptarray_locate_along(const POINTARRAY *pa, double m, double offset) { uint32_t i; POINT4D p1, p2, pn; POINTARRAY *dpa = NULL; /* Can't do anything with degenerate point arrays */ if (!pa || pa->npoints < 2) return NULL; /* Walk through each segment in the point array */ for (i = 1; i < pa->npoints; i++) { getPoint4d_p(pa, i - 1, &p1); getPoint4d_p(pa, i, &p2); /* No derived point? Move to next segment. */ if (segment_locate_along(&p1, &p2, m, offset, &pn) == LW_FALSE) continue; /* No pointarray, make a fresh one */ if (dpa == NULL) dpa = ptarray_construct_empty(ptarray_has_z(pa), ptarray_has_m(pa), 8); /* Add our new point to the array */ ptarray_append_point(dpa, &pn, 0); } return dpa; } static LWMPOINT * lwline_locate_along(const LWLINE *lwline, double m, double offset) { POINTARRAY *opa = NULL; LWMPOINT *mp = NULL; LWGEOM *lwg = lwline_as_lwgeom(lwline); int hasz, hasm, srid; /* Return degenerates upwards */ if (!lwline) return NULL; /* Create empty return shell */ srid = lwgeom_get_srid(lwg); hasz = lwgeom_has_z(lwg); hasm = lwgeom_has_m(lwg); if (hasm) { /* Find points along */ opa = ptarray_locate_along(lwline->points, m, offset); } else { LWLINE *lwline_measured = lwline_measured_from_lwline(lwline, 0.0, 1.0); opa = ptarray_locate_along(lwline_measured->points, m, offset); lwline_free(lwline_measured); } /* Return NULL as EMPTY */ if (!opa) return lwmpoint_construct_empty(srid, hasz, hasm); /* Convert pointarray into a multipoint */ mp = lwmpoint_construct(srid, opa); ptarray_free(opa); return mp; } static LWMPOINT * lwmline_locate_along(const LWMLINE *lwmline, double m, double offset) { LWMPOINT *lwmpoint = NULL; LWGEOM *lwg = lwmline_as_lwgeom(lwmline); uint32_t i, j; /* Return degenerates upwards */ if ((!lwmline) || (lwmline->ngeoms < 1)) return NULL; /* Construct return */ lwmpoint = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); /* Locate along each sub-line */ for (i = 0; i < lwmline->ngeoms; i++) { LWMPOINT *along = lwline_locate_along(lwmline->geoms[i], m, offset); if (along) { if (!lwgeom_is_empty((LWGEOM *)along)) { for (j = 0; j < along->ngeoms; j++) { lwmpoint_add_lwpoint(lwmpoint, along->geoms[j]); } } /* Free the containing geometry, but leave the sub-geometries around */ along->ngeoms = 0; lwmpoint_free(along); } } return lwmpoint; } static LWMPOINT * lwpoint_locate_along(const LWPOINT *lwpoint, double m, __attribute__((__unused__)) double offset) { double point_m = lwpoint_get_m(lwpoint); LWGEOM *lwg = lwpoint_as_lwgeom(lwpoint); LWMPOINT *r = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); if (FP_EQUALS(m, point_m)) { lwmpoint_add_lwpoint(r, lwpoint_clone(lwpoint)); } return r; } static LWMPOINT * lwmpoint_locate_along(const LWMPOINT *lwin, double m, __attribute__((__unused__)) double offset) { LWGEOM *lwg = lwmpoint_as_lwgeom(lwin); LWMPOINT *lwout = NULL; uint32_t i; /* Construct return */ lwout = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); for (i = 0; i < lwin->ngeoms; i++) { double point_m = lwpoint_get_m(lwin->geoms[i]); if (FP_EQUALS(m, point_m)) { lwmpoint_add_lwpoint(lwout, lwpoint_clone(lwin->geoms[i])); } } return lwout; } LWGEOM * lwgeom_locate_along(const LWGEOM *lwin, double m, double offset) { if (!lwin) return NULL; if (!lwgeom_has_m(lwin)) lwerror("Input geometry does not have a measure dimension"); switch (lwin->type) { case POINTTYPE: return (LWGEOM *)lwpoint_locate_along((LWPOINT *)lwin, m, offset); case MULTIPOINTTYPE: return (LWGEOM *)lwmpoint_locate_along((LWMPOINT *)lwin, m, offset); case LINETYPE: return (LWGEOM *)lwline_locate_along((LWLINE *)lwin, m, offset); case MULTILINETYPE: return (LWGEOM *)lwmline_locate_along((LWMLINE *)lwin, m, offset); /* Only line types supported right now */ /* TO DO: CurveString, CompoundCurve, MultiCurve */ /* TO DO: Point, MultiPoint */ default: lwerror("Only linear geometries are supported, %s provided.", lwtype_name(lwin->type)); return NULL; } return NULL; } /** * Given a POINT4D and an ordinate number, return * the value of the ordinate. * @param p input point * @param ordinate number (1=x, 2=y, 3=z, 4=m) * @return d value at that ordinate */ inline double lwpoint_get_ordinate(const POINT4D *p, char ordinate) { if (!p) { lwerror("Null input geometry."); return 0.0; } switch (ordinate) { case 'X': return p->x; case 'Y': return p->y; case 'Z': return p->z; case 'M': return p->m; } lwerror("Cannot extract %c ordinate.", ordinate); return 0.0; } /** * Given a point, ordinate number and value, set that ordinate on the * point. */ inline void lwpoint_set_ordinate(POINT4D *p, char ordinate, double value) { if (!p) { lwerror("Null input geometry."); return; } switch (ordinate) { case 'X': p->x = value; return; case 'Y': p->y = value; return; case 'Z': p->z = value; return; case 'M': p->m = value; return; } lwerror("Cannot set %c ordinate.", ordinate); return; } /** * Given two points, a dimensionality, an ordinate, and an interpolation value * generate a new point that is proportionally between the input points, * using the values in the provided dimension as the scaling factors. */ inline int point_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int hasz, int hasm, char ordinate, double interpolation_value) { static char *dims = "XYZM"; double p1_value = lwpoint_get_ordinate(p1, ordinate); double p2_value = lwpoint_get_ordinate(p2, ordinate); double proportion; int i = 0; #if PARANOIA_LEVEL > 0 if (!(ordinate == 'X' || ordinate == 'Y' || ordinate == 'Z' || ordinate == 'M')) { lwerror("Cannot interpolate over %c ordinate.", ordinate); return LW_FAILURE; } if (FP_MIN(p1_value, p2_value) > interpolation_value || FP_MAX(p1_value, p2_value) < interpolation_value) { lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value); return LW_FAILURE; } #endif proportion = (interpolation_value - p1_value) / (p2_value - p1_value); for (i = 0; i < 4; i++) { if (dims[i] == 'Z' && !hasz) continue; if (dims[i] == 'M' && !hasm) continue; if (dims[i] == ordinate) lwpoint_set_ordinate(p, dims[i], interpolation_value); else { double newordinate = 0.0; p1_value = lwpoint_get_ordinate(p1, dims[i]); p2_value = lwpoint_get_ordinate(p2, dims[i]); newordinate = p1_value + proportion * (p2_value - p1_value); lwpoint_set_ordinate(p, dims[i], newordinate); } } return LW_SUCCESS; } /** * Clip an input POINT between two values, on any ordinate input. */ static inline LWCOLLECTION * lwpoint_clip_to_ordinate_range(const LWPOINT *point, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; char hasz, hasm; POINT4D p4d; double ordinate_value; /* Read Z/M info */ hasz = lwgeom_has_z(lwpoint_as_lwgeom(point)); hasm = lwgeom_has_m(lwpoint_as_lwgeom(point)); /* Prepare return object */ lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, point->srid, hasz, hasm); /* Test if ordinate is in range */ lwpoint_getPoint4d_p(point, &p4d); ordinate_value = lwpoint_get_ordinate(&p4d, ordinate); if (from <= ordinate_value && to >= ordinate_value) { LWPOINT *lwp = lwpoint_clone(point); lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp)); } return lwgeom_out; } /** * Clip an input MULTIPOINT between two values, on any ordinate input. */ static inline LWCOLLECTION * lwmpoint_clip_to_ordinate_range(const LWMPOINT *mpoint, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; char hasz, hasm; uint32_t i; /* Read Z/M info */ hasz = lwgeom_has_z(lwmpoint_as_lwgeom(mpoint)); hasm = lwgeom_has_m(lwmpoint_as_lwgeom(mpoint)); /* Prepare return object */ lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, mpoint->srid, hasz, hasm); /* For each point, is its ordinate value between from and to? */ for (i = 0; i < mpoint->ngeoms; i++) { POINT4D p4d; double ordinate_value; lwpoint_getPoint4d_p(mpoint->geoms[i], &p4d); ordinate_value = lwpoint_get_ordinate(&p4d, ordinate); if (from <= ordinate_value && to >= ordinate_value) { LWPOINT *lwp = lwpoint_clone(mpoint->geoms[i]); lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp)); } } /* Set the bbox, if necessary */ if (mpoint->bbox) lwgeom_refresh_bbox((LWGEOM *)lwgeom_out); return lwgeom_out; } static inline POINTARRAY * ptarray_clamp_to_ordinate_range(const POINTARRAY *ipa, char ordinate, double from, double to, uint8_t is_closed) { POINT4D p1, p2; POINTARRAY *opa; double ovp1, ovp2; POINT4D *t; int8_t p1out, p2out; /* -1 - smaller than from, 0 - in range, 1 - larger than to */ uint32_t i; uint8_t hasz = FLAGS_GET_Z(ipa->flags); uint8_t hasm = FLAGS_GET_M(ipa->flags); assert(from <= to); t = lwalloc(sizeof(POINT4D)); /* Initial storage */ opa = ptarray_construct_empty(hasz, hasm, ipa->npoints); /* Add first point */ getPoint4d_p(ipa, 0, &p1); ovp1 = lwpoint_get_ordinate(&p1, ordinate); p1out = (ovp1 < from) ? -1 : ((ovp1 > to) ? 1 : 0); if (from <= ovp1 && ovp1 <= to) ptarray_append_point(opa, &p1, LW_FALSE); /* Loop on all other input points */ for (i = 1; i < ipa->npoints; i++) { getPoint4d_p(ipa, i, &p2); ovp2 = lwpoint_get_ordinate(&p2, ordinate); p2out = (ovp2 < from) ? -1 : ((ovp2 > to) ? 1 : 0); if (p1out == 0 && p2out == 0) /* both visible */ { ptarray_append_point(opa, &p2, LW_FALSE); } else if (p1out == p2out && p1out != 0) /* both invisible on the same side */ { /* skip */ } else if (p1out == -1 && p2out == 0) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from); ptarray_append_point(opa, t, LW_FALSE); ptarray_append_point(opa, &p2, LW_FALSE); } else if (p1out == -1 && p2out == 1) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from); ptarray_append_point(opa, t, LW_FALSE); point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to); ptarray_append_point(opa, t, LW_FALSE); } else if (p1out == 0 && p2out == -1) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from); ptarray_append_point(opa, t, LW_FALSE); } else if (p1out == 0 && p2out == 1) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to); ptarray_append_point(opa, t, LW_FALSE); } else if (p1out == 1 && p2out == -1) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to); ptarray_append_point(opa, t, LW_FALSE); point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from); ptarray_append_point(opa, t, LW_FALSE); } else if (p1out == 1 && p2out == 0) { point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to); ptarray_append_point(opa, t, LW_FALSE); ptarray_append_point(opa, &p2, LW_FALSE); } p1 = p2; p1out = p2out; LW_ON_INTERRUPT(ptarray_free(opa); return NULL); } if (is_closed && opa->npoints > 2) { getPoint4d_p(opa, 0, &p1); ptarray_append_point(opa, &p1, LW_FALSE); } lwfree(t); return opa; } /** * Take in a LINESTRING and return a MULTILINESTRING of those portions of the * LINESTRING between the from/to range for the specified ordinate (XYZM) */ static inline LWCOLLECTION * lwline_clip_to_ordinate_range(const LWLINE *line, char ordinate, double from, double to) { POINTARRAY *pa_in = NULL; LWCOLLECTION *lwgeom_out = NULL; POINTARRAY *dp = NULL; uint32_t i; int added_last_point = 0; POINT4D *p = NULL, *q = NULL, *r = NULL; double ordinate_value_p = 0.0, ordinate_value_q = 0.0; char hasz, hasm; char dims; /* Null input, nothing we can do. */ assert(line); hasz = lwgeom_has_z(lwline_as_lwgeom(line)); hasm = lwgeom_has_m(lwline_as_lwgeom(line)); dims = FLAGS_NDIMS(line->flags); /* Asking for an ordinate we don't have. Error. */ if ((ordinate == 'Z' && !hasz) || (ordinate == 'M' && !hasm)) { lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims); return NULL; } /* Prepare our working point objects. */ p = lwalloc(sizeof(POINT4D)); q = lwalloc(sizeof(POINT4D)); r = lwalloc(sizeof(POINT4D)); /* Construct a collection to hold our outputs. */ lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm); /* Get our input point array */ pa_in = line->points; for (i = 0; i < pa_in->npoints; i++) { if (i > 0) { *q = *p; ordinate_value_q = ordinate_value_p; } getPoint4d_p(pa_in, i, p); ordinate_value_p = lwpoint_get_ordinate(p, ordinate); /* Is this point inside the ordinate range? Yes. */ if (ordinate_value_p >= from && ordinate_value_p <= to) { if (!added_last_point) { /* We didn't add the previous point, so this is a new segment. * Make a new point array. */ dp = ptarray_construct_empty(hasz, hasm, 32); /* We're transiting into the range so add an interpolated * point at the range boundary. * If we're on a boundary and crossing from the far side, * we also need an interpolated point. */ if (i > 0 && (/* Don't try to interpolate if this is the first point */ (ordinate_value_p > from && ordinate_value_p < to) || /* Inside */ (ordinate_value_p == from && ordinate_value_q > to) || /* Hopping from above */ (ordinate_value_p == to && ordinate_value_q < from))) /* Hopping from below */ { double interpolation_value; (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(dp, r, LW_FALSE); } } /* Add the current vertex to the point array. */ ptarray_append_point(dp, p, LW_FALSE); if (ordinate_value_p == from || ordinate_value_p == to) added_last_point = 2; /* Added on boundary. */ else added_last_point = 1; /* Added inside range. */ } /* Is this point inside the ordinate range? No. */ else { if (added_last_point == 1) { /* We're transiting out of the range, so add an interpolated point * to the point array at the range boundary. */ double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(dp, r, LW_FALSE); } else if (added_last_point == 2) { /* We're out and the last point was on the boundary. * If the last point was the near boundary, nothing to do. * If it was the far boundary, we need an interpolated point. */ if (from != to && ((ordinate_value_q == from && ordinate_value_p > from) || (ordinate_value_q == to && ordinate_value_p < to))) { double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(dp, r, LW_FALSE); } } else if (i && ordinate_value_q < from && ordinate_value_p > to) { /* We just hopped over the whole range, from bottom to top, * so we need to add *two* interpolated points! */ dp = ptarray_construct(hasz, hasm, 2); /* Interpolate lower point. */ point_interpolate(p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(dp, 0, r); /* Interpolate upper point. */ point_interpolate(p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(dp, 1, r); } else if (i && ordinate_value_q > to && ordinate_value_p < from) { /* We just hopped over the whole range, from top to bottom, * so we need to add *two* interpolated points! */ dp = ptarray_construct(hasz, hasm, 2); /* Interpolate upper point. */ point_interpolate(p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(dp, 0, r); /* Interpolate lower point. */ point_interpolate(p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(dp, 1, r); } /* We have an extant point-array, save it out to a multi-line. */ if (dp) { /* Only one point, so we have to make an lwpoint to hold this * and set the overall output type to a generic collection. */ if (dp->npoints == 1) { LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); lwgeom_out->type = COLLECTIONTYPE; lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); } else { LWLINE *oline = lwline_construct(line->srid, NULL, dp); lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); } /* Pointarray is now owned by lwgeom_out, so drop reference to it */ dp = NULL; } added_last_point = 0; } } /* Still some points left to be saved out. */ if (dp) { if (dp->npoints == 1) { LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); lwgeom_out->type = COLLECTIONTYPE; lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); } else if (dp->npoints > 1) { LWLINE *oline = lwline_construct(line->srid, NULL, dp); lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); } else ptarray_free(dp); } lwfree(p); lwfree(q); lwfree(r); if (line->bbox && lwgeom_out->ngeoms > 0) lwgeom_refresh_bbox((LWGEOM *)lwgeom_out); return lwgeom_out; } /** * Clip an input LWPOLY between two values, on any ordinate input. */ static inline LWCOLLECTION * lwpoly_clip_to_ordinate_range(const LWPOLY *poly, char ordinate, double from, double to) { assert(poly); char hasz = FLAGS_GET_Z(poly->flags), hasm = FLAGS_GET_M(poly->flags); LWPOLY *poly_res = lwpoly_construct_empty(poly->srid, hasz, hasm); LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(MULTIPOLYGONTYPE, poly->srid, hasz, hasm); for (uint32_t i = 0; i < poly->nrings; i++) { /* Ret number of points */ POINTARRAY *pa = ptarray_clamp_to_ordinate_range(poly->rings[i], ordinate, from, to, LW_TRUE); if (pa->npoints >= 4) lwpoly_add_ring(poly_res, pa); else { ptarray_free(pa); if (i == 0) break; } } if (poly_res->nrings > 0) lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)poly_res); else lwpoly_free(poly_res); return lwgeom_out; } /** * Clip an input LWTRIANGLE between two values, on any ordinate input. */ static inline LWCOLLECTION * lwtriangle_clip_to_ordinate_range(const LWTRIANGLE *tri, char ordinate, double from, double to) { assert(tri); char hasz = FLAGS_GET_Z(tri->flags), hasm = FLAGS_GET_M(tri->flags); LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(TINTYPE, tri->srid, hasz, hasm); POINTARRAY *pa = ptarray_clamp_to_ordinate_range(tri->points, ordinate, from, to, LW_TRUE); if (pa->npoints >= 4) { POINT4D first = getPoint4d(pa, 0); for (uint32_t i = 1; i < pa->npoints - 2; i++) { POINT4D p; POINTARRAY *tpa = ptarray_construct_empty(hasz, hasm, 4); ptarray_append_point(tpa, &first, LW_TRUE); getPoint4d_p(pa, i, &p); ptarray_append_point(tpa, &p, LW_TRUE); getPoint4d_p(pa, i + 1, &p); ptarray_append_point(tpa, &p, LW_TRUE); ptarray_append_point(tpa, &first, LW_TRUE); LWTRIANGLE *otri = lwtriangle_construct(tri->srid, NULL, tpa); lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)otri); } } ptarray_free(pa); return lwgeom_out; } /** * Clip an input COLLECTION between two values, on any ordinate input. */ static inline LWCOLLECTION * lwcollection_clip_to_ordinate_range(const LWCOLLECTION *icol, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out; assert(icol); if (icol->ngeoms == 1) lwgeom_out = lwgeom_clip_to_ordinate_range(icol->geoms[0], ordinate, from, to, 0); else { LWCOLLECTION *col; char hasz = lwgeom_has_z(lwcollection_as_lwgeom(icol)); char hasm = lwgeom_has_m(lwcollection_as_lwgeom(icol)); uint32_t i; lwgeom_out = lwcollection_construct_empty(icol->type, icol->srid, hasz, hasm); FLAGS_SET_Z(lwgeom_out->flags, hasz); FLAGS_SET_M(lwgeom_out->flags, hasm); for (i = 0; i < icol->ngeoms; i++) { col = lwgeom_clip_to_ordinate_range(icol->geoms[i], ordinate, from, to, 0); if (col) { if (col->type != icol->type) lwgeom_out->type = COLLECTIONTYPE; lwgeom_out = lwcollection_concat_in_place(lwgeom_out, col); lwfree(col->geoms); lwcollection_release(col); } } } if (icol->bbox) lwgeom_refresh_bbox((LWGEOM *)lwgeom_out); return lwgeom_out; } LWCOLLECTION * lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset) { LWCOLLECTION *out_col; LWCOLLECTION *out_offset; uint32_t i; /* Ensure 'from' is less than 'to'. */ if (to < from) { double t = from; from = to; to = t; } if (!lwin) lwerror("lwgeom_clip_to_ordinate_range: null input geometry!"); switch (lwin->type) { case LINETYPE: out_col = lwline_clip_to_ordinate_range((LWLINE *)lwin, ordinate, from, to); break; case MULTIPOINTTYPE: out_col = lwmpoint_clip_to_ordinate_range((LWMPOINT *)lwin, ordinate, from, to); break; case POINTTYPE: out_col = lwpoint_clip_to_ordinate_range((LWPOINT *)lwin, ordinate, from, to); break; case POLYGONTYPE: out_col = lwpoly_clip_to_ordinate_range((LWPOLY *)lwin, ordinate, from, to); break; case TRIANGLETYPE: out_col = lwtriangle_clip_to_ordinate_range((LWTRIANGLE *)lwin, ordinate, from, to); break; case TINTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case POLYHEDRALSURFACETYPE: out_col = lwcollection_clip_to_ordinate_range((LWCOLLECTION *)lwin, ordinate, from, to); break; default: lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type)); return NULL; } /* Stop if result is NULL */ if (!out_col) lwerror("lwgeom_clip_to_ordinate_range clipping routine returned NULL"); /* Return if we aren't going to offset the result */ if (FP_IS_ZERO(offset) || lwgeom_is_empty(lwcollection_as_lwgeom(out_col))) return out_col; /* Construct a collection to hold our outputs. */ /* Things get ugly: GEOS offset drops Z's and M's so we have to drop ours */ out_offset = lwcollection_construct_empty(MULTILINETYPE, lwin->srid, 0, 0); /* Try and offset the linear portions of the return value */ for (i = 0; i < out_col->ngeoms; i++) { int type = out_col->geoms[i]->type; if (type == POINTTYPE) { lwnotice("lwgeom_clip_to_ordinate_range cannot offset a clipped point"); continue; } else if (type == LINETYPE) { /* lwgeom_offsetcurve(line, offset, quadsegs, joinstyle (round), mitrelimit) */ LWGEOM *lwoff = lwgeom_offsetcurve(out_col->geoms[i], offset, 8, 1, 5.0); if (!lwoff) { lwerror("lwgeom_offsetcurve returned null"); } lwcollection_add_lwgeom(out_offset, lwoff); } else { lwerror("lwgeom_clip_to_ordinate_range found an unexpected type (%s) in the offset routine", lwtype_name(type)); } } return out_offset; } LWCOLLECTION * lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset) { if (!lwgeom_has_m(lwin)) lwerror("Input geometry does not have a measure dimension"); return lwgeom_clip_to_ordinate_range(lwin, 'M', from, to, offset); } double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt) { POINT4D p, p_proj; double ret = 0.0; if (!lwin) lwerror("lwgeom_interpolate_point: null input geometry!"); if (!lwgeom_has_m(lwin)) lwerror("Input geometry does not have a measure dimension"); if (lwgeom_is_empty(lwin) || lwpoint_is_empty(lwpt)) lwerror("Input geometry is empty"); switch (lwin->type) { case LINETYPE: { LWLINE *lwline = lwgeom_as_lwline(lwin); lwpoint_getPoint4d_p(lwpt, &p); ret = ptarray_locate_point(lwline->points, &p, NULL, &p_proj); ret = p_proj.m; break; } default: lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type)); } return ret; } /* * Time of closest point of approach * * Given two vectors (p1-p2 and q1-q2) and * a time range (t1-t2) return the time in which * a point p is closest to a point q on their * respective vectors, and the actual points * * Here we use algorithm from softsurfer.com * that can be found here * http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm * * @param p0 start of first segment, will be set to actual * closest point of approach on segment. * @param p1 end of first segment * @param q0 start of second segment, will be set to actual * closest point of approach on segment. * @param q1 end of second segment * @param t0 start of travel time * @param t1 end of travel time * * @return time of closest point of approach * */ static double segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1) { POINT3DZ pv; /* velocity of p, aka u */ POINT3DZ qv; /* velocity of q, aka v */ POINT3DZ dv; /* velocity difference */ POINT3DZ w0; /* vector between first points */ /* lwnotice("FROM %g,%g,%g,%g -- %g,%g,%g,%g", p0->x, p0->y, p0->z, p0->m, p1->x, p1->y, p1->z, p1->m); lwnotice(" TO %g,%g,%g,%g -- %g,%g,%g,%g", q0->x, q0->y, q0->z, q0->m, q1->x, q1->y, q1->z, q1->m); */ /* PV aka U */ pv.x = (p1->x - p0->x); pv.y = (p1->y - p0->y); pv.z = (p1->z - p0->z); /*lwnotice("PV: %g, %g, %g", pv.x, pv.y, pv.z);*/ /* QV aka V */ qv.x = (q1->x - q0->x); qv.y = (q1->y - q0->y); qv.z = (q1->z - q0->z); /*lwnotice("QV: %g, %g, %g", qv.x, qv.y, qv.z);*/ dv.x = pv.x - qv.x; dv.y = pv.y - qv.y; dv.z = pv.z - qv.z; /*lwnotice("DV: %g, %g, %g", dv.x, dv.y, dv.z);*/ double dv2 = DOT(dv, dv); /*lwnotice("DOT: %g", dv2);*/ if (dv2 == 0.0) { /* Distance is the same at any time, we pick the earliest */ return t0; } /* Distance at any given time, with t0 */ w0.x = (p0->x - q0->x); w0.y = (p0->y - q0->y); w0.z = (p0->z - q0->z); /*lwnotice("W0: %g, %g, %g", w0.x, w0.y, w0.z);*/ /* Check that at distance dt w0 is distance */ /* This is the fraction of measure difference */ double t = -DOT(w0, dv) / dv2; /*lwnotice("CLOSEST TIME (fraction): %g", t);*/ if (t > 1.0) { /* Getting closer as we move to the end */ /*lwnotice("Converging");*/ t = 1; } else if (t < 0.0) { /*lwnotice("Diverging");*/ t = 0; } /* Interpolate the actual points now */ p0->x += pv.x * t; p0->y += pv.y * t; p0->z += pv.z * t; q0->x += qv.x * t; q0->y += qv.y * t; q0->z += qv.z * t; t = t0 + (t1 - t0) * t; /*lwnotice("CLOSEST TIME (real): %g", t);*/ return t; } static int ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals) { POINT4D pbuf; uint32_t i, n = 0; for (i = 0; i < pa->npoints; ++i) { getPoint4d_p(pa, i, &pbuf); /* could be optimized */ if (pbuf.m >= tmin && pbuf.m <= tmax) mvals[n++] = pbuf.m; } return n; } static int compare_double(const void *pa, const void *pb) { double a = *((double *)pa); double b = *((double *)pb); if (a < b) return -1; else if (a > b) return 1; else return 0; } /* Return number of elements in unique array */ static int uniq(double *vals, int nvals) { int i, last = 0; for (i = 1; i < nvals; ++i) { // lwnotice("(I%d):%g", i, vals[i]); if (vals[i] != vals[last]) { vals[++last] = vals[i]; // lwnotice("(O%d):%g", last, vals[last]); } } return last + 1; } /* * Find point at a given measure * * The function assumes measures are linear so that always a single point * is returned for a single measure. * * @param pa the point array to perform search on * @param m the measure to search for * @param p the point to write result into * @param from the segment number to start from * * @return the segment number the point was found into * or -1 if given measure was out of the known range. */ static int ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from) { uint32_t i = from; POINT4D p1, p2; /* Walk through each segment in the point array */ getPoint4d_p(pa, i, &p1); for (i = from + 1; i < pa->npoints; i++) { getPoint4d_p(pa, i, &p2); if (segment_locate_along(&p1, &p2, m, 0, p) == LW_TRUE) return i - 1; /* found */ p1 = p2; } return -1; /* not found */ } double lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist) { LWLINE *l1, *l2; int i; GBOX gbox1, gbox2; double tmin, tmax; double *mvals; int nmvals = 0; double mintime; double mindist2 = FLT_MAX; /* minimum distance, squared */ if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2)) { lwerror("Both input geometries must have a measure dimension"); return -1; } l1 = lwgeom_as_lwline(g1); l2 = lwgeom_as_lwline(g2); if (!l1 || !l2) { lwerror("Both input geometries must be linestrings"); return -1; } if (l1->points->npoints < 2 || l2->points->npoints < 2) { lwerror("Both input lines must have at least 2 points"); return -1; } /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */ /* because we cannot afford the float rounding inaccuracy when */ /* we compare the ranges for overlap below */ lwgeom_calculate_gbox(g1, &gbox1); lwgeom_calculate_gbox(g2, &gbox2); /* * Find overlapping M range * WARNING: may be larger than the real one */ tmin = FP_MAX(gbox1.mmin, gbox2.mmin); tmax = FP_MIN(gbox1.mmax, gbox2.mmax); if (tmax < tmin) { LWDEBUG(1, "Inputs never exist at the same time"); return -2; } // lwnotice("Min:%g, Max:%g", tmin, tmax); /* * Collect M values in common time range from inputs */ mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints)); /* TODO: also clip the lines ? */ nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals); nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals); /* Sort values in ascending order */ qsort(mvals, nmvals, sizeof(double), compare_double); /* Remove duplicated values */ nmvals = uniq(mvals, nmvals); if (nmvals < 2) { { /* there's a single time, must be that one... */ double t0 = mvals[0]; POINT4D p0, p1; LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0); if (mindist) { if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0)) { lwfree(mvals); lwerror("Could not find point with M=%g on first geom", t0); return -1; } if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0)) { lwfree(mvals); lwerror("Could not find point with M=%g on second geom", t0); return -1; } *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1); } lwfree(mvals); return t0; } } /* * For each consecutive pair of measures, compute time of closest point * approach and actual distance between points at that time */ mintime = tmin; for (i = 1; i < nmvals; ++i) { double t0 = mvals[i - 1]; double t1 = mvals[i]; double t; POINT4D p0, p1, q0, q1; int seg; double dist2; // lwnotice("T %g-%g", t0, t1); seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z); seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z); seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z); seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z); t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1); /* lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g", p0.x, p0.y, p0.z, q0.x, q0.y, q0.z, t); */ dist2 = (q0.x - p0.x) * (q0.x - p0.x) + (q0.y - p0.y) * (q0.y - p0.y) + (q0.z - p0.z) * (q0.z - p0.z); if (dist2 < mindist2) { mindist2 = dist2; mintime = t; // lwnotice("MINTIME: %g", mintime); } } /* * Release memory */ lwfree(mvals); if (mindist) { *mindist = sqrt(mindist2); } /*lwnotice("MINDIST: %g", sqrt(mindist2));*/ return mintime; } int lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist) { LWLINE *l1, *l2; int i; GBOX gbox1, gbox2; double tmin, tmax; double *mvals; int nmvals = 0; double maxdist2 = maxdist * maxdist; int within = LW_FALSE; if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2)) { lwerror("Both input geometries must have a measure dimension"); return LW_FALSE; } l1 = lwgeom_as_lwline(g1); l2 = lwgeom_as_lwline(g2); if (!l1 || !l2) { lwerror("Both input geometries must be linestrings"); return LW_FALSE; } if (l1->points->npoints < 2 || l2->points->npoints < 2) { /* TODO: return distance between these two points */ lwerror("Both input lines must have at least 2 points"); return LW_FALSE; } /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */ /* because we cannot afford the float rounding inaccuracy when */ /* we compare the ranges for overlap below */ lwgeom_calculate_gbox(g1, &gbox1); lwgeom_calculate_gbox(g2, &gbox2); /* * Find overlapping M range * WARNING: may be larger than the real one */ tmin = FP_MAX(gbox1.mmin, gbox2.mmin); tmax = FP_MIN(gbox1.mmax, gbox2.mmax); if (tmax < tmin) { LWDEBUG(1, "Inputs never exist at the same time"); return LW_FALSE; } /* * Collect M values in common time range from inputs */ mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints)); /* TODO: also clip the lines ? */ nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals); nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals); /* Sort values in ascending order */ qsort(mvals, nmvals, sizeof(double), compare_double); /* Remove duplicated values */ nmvals = uniq(mvals, nmvals); if (nmvals < 2) { /* there's a single time, must be that one... */ double t0 = mvals[0]; POINT4D p0, p1; LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0); if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0)) { lwnotice("Could not find point with M=%g on first geom", t0); return LW_FALSE; } if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0)) { lwnotice("Could not find point with M=%g on second geom", t0); return LW_FALSE; } if (distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1) <= maxdist) within = LW_TRUE; lwfree(mvals); return within; } /* * For each consecutive pair of measures, compute time of closest point * approach and actual distance between points at that time */ for (i = 1; i < nmvals; ++i) { double t0 = mvals[i - 1]; double t1 = mvals[i]; #if POSTGIS_DEBUG_LEVEL >= 1 double t; #endif POINT4D p0, p1, q0, q1; int seg; double dist2; // lwnotice("T %g-%g", t0, t1); seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z); seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z); seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z); seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg); if (-1 == seg) continue; /* possible, if GBOX is approximated */ // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z); #if POSTGIS_DEBUG_LEVEL >= 1 t = #endif segments_tcpa(&p0, &p1, &q0, &q1, t0, t1); /* lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g", p0.x, p0.y, p0.z, q0.x, q0.y, q0.z, t); */ dist2 = (q0.x - p0.x) * (q0.x - p0.x) + (q0.y - p0.y) * (q0.y - p0.y) + (q0.z - p0.z) * (q0.z - p0.z); if (dist2 <= maxdist2) { LWDEBUGF(1, "Within distance %g at time %g, breaking", sqrt(dist2), t); within = LW_TRUE; break; } } /* * Release memory */ lwfree(mvals); return within; } lwgeom/src/liblwgeom/lwchaikins.c0000644000176200001440000001171313773172540016630 0ustar liggesusers /********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2018 Nicklas Avén * **********************************************************************/ #include "liblwgeom_internal.h" static POINTARRAY * ptarray_chaikin(POINTARRAY *inpts, int preserve_endpoint, int isclosed) { uint32_t p, i, n_out_points=0, p1_set=0, p2_set=0; POINT4D p1, p2; POINTARRAY *opts; double *dlist; double deltaval, quarter_delta, val1, val2; uint32_t ndims = 2 + ptarray_has_z(inpts) + ptarray_has_m(inpts); int new_npoints = inpts->npoints * 2; opts = ptarray_construct_empty(FLAGS_GET_Z(inpts->flags), FLAGS_GET_M(inpts->flags), new_npoints); dlist = (double*)(opts->serialized_pointlist); p1 = getPoint4d(inpts, 0); /*if first point*/ if(preserve_endpoint) { ptarray_append_point(opts, &p1, LW_TRUE); n_out_points++; } for (p=1;pnpoints;p++) { memcpy(&p2, &p1, sizeof(POINT4D)); p1 = getPoint4d(inpts, p); if(p>0) { p1_set = p2_set = 0; for (i=0;i 1) { dlist[n_out_points * ndims + i] = val2 + quarter_delta; p1_set = 1; } if(!preserve_endpoint || p < inpts->npoints - 1) { dlist[(n_out_points + p1_set) * ndims + i] = val1 - quarter_delta; p2_set = 1; } } n_out_points+=(p1_set + p2_set); } } /*if last point*/ if(preserve_endpoint) { opts->npoints = n_out_points; ptarray_append_point(opts, &p1, LW_TRUE); n_out_points++; } if(isclosed && !preserve_endpoint) { opts->npoints = n_out_points; POINT4D first_point = getPoint4d(opts, 0); ptarray_append_point(opts, &first_point, LW_TRUE); n_out_points++; } opts->npoints = n_out_points; return opts; } static LWLINE* lwline_chaikin(const LWLINE *iline, int n_iterations) { POINTARRAY *pa, *pa_new; int j; LWLINE *oline; if( lwline_is_empty(iline)) return lwline_clone(iline); pa = iline->points; for (j=0;j0) ptarray_free(pa); pa=pa_new; } oline = lwline_construct(iline->srid, NULL, pa); oline->type = iline->type; return oline; } static LWPOLY* lwpoly_chaikin(const LWPOLY *ipoly, int n_iterations, int preserve_endpoint) { uint32_t i; int j; POINTARRAY *pa, *pa_new; LWPOLY *opoly = lwpoly_construct_empty(ipoly->srid, FLAGS_GET_Z(ipoly->flags), FLAGS_GET_M(ipoly->flags)); if( lwpoly_is_empty(ipoly) ) return opoly; for (i = 0; i < ipoly->nrings; i++) { pa = ipoly->rings[i]; for(j=0;j0) ptarray_free(pa); pa=pa_new; } if(pa->npoints>=4) { if( lwpoly_add_ring(opoly,pa ) == LW_FAILURE ) return NULL; } } opoly->type = ipoly->type; if( lwpoly_is_empty(opoly) ) return NULL; return opoly; } static LWCOLLECTION* lwcollection_chaikin(const LWCOLLECTION *igeom, int n_iterations, int preserve_endpoint) { LWDEBUG(2, "Entered lwcollection_set_effective_area"); uint32_t i; LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags), FLAGS_GET_M(igeom->flags)); if( lwcollection_is_empty(igeom) ) return out; /* should we return NULL instead ? */ for( i = 0; i < igeom->ngeoms; i++ ) { LWGEOM *ngeom = lwgeom_chaikin(igeom->geoms[i],n_iterations,preserve_endpoint); if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom); } return out; } LWGEOM* lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint) { LWDEBUG(2, "Entered lwgeom_set_effective_area"); switch (igeom->type) { case POINTTYPE: case MULTIPOINTTYPE: return lwgeom_clone(igeom); case LINETYPE: return (LWGEOM*)lwline_chaikin((LWLINE*)igeom, n_iterations); case POLYGONTYPE: return (LWGEOM*)lwpoly_chaikin((LWPOLY*)igeom,n_iterations,preserve_endpoint); case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return (LWGEOM*)lwcollection_chaikin((LWCOLLECTION *)igeom,n_iterations,preserve_endpoint); default: lwerror("lwgeom_chaikin: unsupported geometry type: %s",lwtype_name(igeom->type)); } return NULL; } lwgeom/src/liblwgeom/lwgeom.c0000644000176200001440000015407213773172540015774 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * Copyright (C) 2017-2018 Daniel Baston * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" /** Force Right-hand-rule on LWGEOM polygons **/ void lwgeom_force_clockwise(LWGEOM *lwgeom) { LWCOLLECTION *coll; uint32_t i; switch (lwgeom->type) { case POLYGONTYPE: lwpoly_force_clockwise((LWPOLY *)lwgeom); return; case TRIANGLETYPE: lwtriangle_force_clockwise((LWTRIANGLE *)lwgeom); return; /* Not handle POLYHEDRALSURFACE and TIN as they are supposed to be well oriented */ case MULTIPOLYGONTYPE: case COLLECTIONTYPE: coll = (LWCOLLECTION *)lwgeom; for (i=0; ingeoms; i++) lwgeom_force_clockwise(coll->geoms[i]); return; } } /** Check clockwise orientation on LWGEOM polygons **/ int lwgeom_is_clockwise(LWGEOM *lwgeom) { switch (lwgeom->type) { case POLYGONTYPE: return lwpoly_is_clockwise((LWPOLY *)lwgeom); case TRIANGLETYPE: return lwtriangle_is_clockwise((LWTRIANGLE *)lwgeom); case MULTIPOLYGONTYPE: case COLLECTIONTYPE: { uint32_t i; LWCOLLECTION* coll = (LWCOLLECTION *)lwgeom; for (i=0; i < coll->ngeoms; i++) if (!lwgeom_is_clockwise(coll->geoms[i])) return LW_FALSE; return LW_TRUE; } default: return LW_TRUE; return LW_FALSE; } } LWGEOM * lwgeom_reverse(const LWGEOM *geom) { LWGEOM *geomout = lwgeom_clone_deep(geom); lwgeom_reverse_in_place(geomout); return geomout; } /** Reverse vertex order of LWGEOM **/ void lwgeom_reverse_in_place(LWGEOM *geom) { uint32_t i; LWCOLLECTION *col; if (!geom) return; switch (geom->type) { case MULTIPOINTTYPE: case POINTTYPE: { return; } case TRIANGLETYPE: case CIRCSTRINGTYPE: case LINETYPE: { LWLINE *line = (LWLINE *)(geom); ptarray_reverse_in_place(line->points); return; } case POLYGONTYPE: { LWPOLY *poly = (LWPOLY *)(geom); if (!poly->rings) return; uint32_t r; for (r = 0; r < poly->nrings; r++) ptarray_reverse_in_place(poly->rings[r]); return; } case MULTICURVETYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: { col = (LWCOLLECTION *)(geom); if (!col->geoms) return; for (i=0; ingeoms; i++) lwgeom_reverse_in_place(col->geoms[i]); return; } default: { lwerror("%s: Unknown geometry type: %s", __func__, lwtype_name(geom->type)); return; } } } LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == LINETYPE ) return (LWLINE *)lwgeom; else return NULL; } LWCIRCSTRING * lwgeom_as_lwcircstring(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == CIRCSTRINGTYPE ) return (LWCIRCSTRING *)lwgeom; else return NULL; } LWCOMPOUND * lwgeom_as_lwcompound(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == COMPOUNDTYPE ) return (LWCOMPOUND *)lwgeom; else return NULL; } LWCURVEPOLY * lwgeom_as_lwcurvepoly(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == CURVEPOLYTYPE ) return (LWCURVEPOLY *)lwgeom; else return NULL; } LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == POLYGONTYPE ) return (LWPOLY *)lwgeom; else return NULL; } LWTRIANGLE * lwgeom_as_lwtriangle(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == TRIANGLETYPE ) return (LWTRIANGLE *)lwgeom; else return NULL; } LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom_is_collection(lwgeom) ) return (LWCOLLECTION*)lwgeom; else return NULL; } LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == MULTIPOINTTYPE ) return (LWMPOINT *)lwgeom; else return NULL; } LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == MULTILINETYPE ) return (LWMLINE *)lwgeom; else return NULL; } LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom) { if ( lwgeom == NULL ) return NULL; if ( lwgeom->type == MULTIPOLYGONTYPE ) return (LWMPOLY *)lwgeom; else return NULL; } LWPSURFACE * lwgeom_as_lwpsurface(const LWGEOM *lwgeom) { if ( lwgeom->type == POLYHEDRALSURFACETYPE ) return (LWPSURFACE *)lwgeom; else return NULL; } LWTIN * lwgeom_as_lwtin(const LWGEOM *lwgeom) { if ( lwgeom->type == TINTYPE ) return (LWTIN *)lwgeom; else return NULL; } LWGEOM *lwtin_as_lwgeom(const LWTIN *obj) { return (LWGEOM *)obj; } LWGEOM *lwpsurface_as_lwgeom(const LWPSURFACE *obj) { return (LWGEOM *)obj; } LWGEOM *lwmpoly_as_lwgeom(const LWMPOLY *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwmline_as_lwgeom(const LWMLINE *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwmpoint_as_lwgeom(const LWMPOINT *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwcollection_as_lwgeom(const LWCOLLECTION *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwcircstring_as_lwgeom(const LWCIRCSTRING *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwcurvepoly_as_lwgeom(const LWCURVEPOLY *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwcompound_as_lwgeom(const LWCOMPOUND *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwpoly_as_lwgeom(const LWPOLY *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwtriangle_as_lwgeom(const LWTRIANGLE *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwline_as_lwgeom(const LWLINE *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } LWGEOM *lwpoint_as_lwgeom(const LWPOINT *obj) { if ( obj == NULL ) return NULL; return (LWGEOM *)obj; } /** ** Look-up for the correct MULTI* type promotion for singleton types. */ uint8_t MULTITYPE[NUMTYPES] = { 0, MULTIPOINTTYPE, /* 1 */ MULTILINETYPE, /* 2 */ MULTIPOLYGONTYPE, /* 3 */ 0,0,0,0, MULTICURVETYPE, /* 8 */ MULTICURVETYPE, /* 9 */ MULTISURFACETYPE, /* 10 */ POLYHEDRALSURFACETYPE, /* 11 */ 0, 0, TINTYPE, /* 14 */ 0 }; uint8_t lwtype_multitype(uint8_t type) { if (type > 15) return 0; return MULTITYPE[type]; } /** * Create a new LWGEOM of the appropriate MULTI* type. */ LWGEOM * lwgeom_as_multi(const LWGEOM *lwgeom) { LWGEOM **ogeoms; LWGEOM *ogeom = NULL; GBOX *box = NULL; int type; type = lwgeom->type; if ( ! MULTITYPE[type] ) return lwgeom_clone(lwgeom); if( lwgeom_is_empty(lwgeom) ) { ogeom = (LWGEOM *)lwcollection_construct_empty( MULTITYPE[type], lwgeom->srid, FLAGS_GET_Z(lwgeom->flags), FLAGS_GET_M(lwgeom->flags) ); } else { ogeoms = lwalloc(sizeof(LWGEOM*)); ogeoms[0] = lwgeom_clone(lwgeom); /* Sub-geometries are not allowed to have bboxes or SRIDs, move the bbox to the collection */ box = ogeoms[0]->bbox; ogeoms[0]->bbox = NULL; ogeoms[0]->srid = SRID_UNKNOWN; ogeom = (LWGEOM *)lwcollection_construct(MULTITYPE[type], lwgeom->srid, box, 1, ogeoms); } return ogeom; } /** * Create a new LWGEOM of the appropriate CURVE* type. */ LWGEOM * lwgeom_as_curve(const LWGEOM *lwgeom) { LWGEOM *ogeom; int type = lwgeom->type; /* int hasz = FLAGS_GET_Z(lwgeom->flags); int hasm = FLAGS_GET_M(lwgeom->flags); int32_t srid = lwgeom->srid; */ switch(type) { case LINETYPE: /* turn to COMPOUNDCURVE */ ogeom = (LWGEOM*)lwcompound_construct_from_lwline((LWLINE*)lwgeom); break; case POLYGONTYPE: ogeom = (LWGEOM*)lwcurvepoly_construct_from_lwpoly(lwgeom_as_lwpoly(lwgeom)); break; case MULTILINETYPE: /* turn to MULTICURVE */ ogeom = lwgeom_clone(lwgeom); ogeom->type = MULTICURVETYPE; break; case MULTIPOLYGONTYPE: /* turn to MULTISURFACE */ ogeom = lwgeom_clone(lwgeom); ogeom->type = MULTISURFACETYPE; break; case COLLECTIONTYPE: default: ogeom = lwgeom_clone(lwgeom); break; } /* TODO: copy bbox from input geom ? */ return ogeom; } /** * Free the containing LWGEOM and the associated BOX. Leave the underlying * geoms/points/point objects intact. Useful for functions that are stripping * out subcomponents of complex objects, or building up new temporary objects * on top of subcomponents. */ void lwgeom_release(LWGEOM *lwgeom) { if ( ! lwgeom ) lwerror("lwgeom_release: someone called on 0x0"); LWDEBUGF(3, "releasing type %s", lwtype_name(lwgeom->type)); /* Drop bounding box (always a copy) */ if ( lwgeom->bbox ) { LWDEBUGF(3, "lwgeom_release: releasing bbox. %p", lwgeom->bbox); lwfree(lwgeom->bbox); } lwfree(lwgeom); } /* @brief Clone LWGEOM object. Serialized point lists are not copied. * * @see ptarray_clone */ LWGEOM * lwgeom_clone(const LWGEOM *lwgeom) { LWDEBUGF(2, "lwgeom_clone called with %p, %s", lwgeom, lwtype_name(lwgeom->type)); switch (lwgeom->type) { case POINTTYPE: return (LWGEOM *)lwpoint_clone((LWPOINT *)lwgeom); case LINETYPE: return (LWGEOM *)lwline_clone((LWLINE *)lwgeom); case CIRCSTRINGTYPE: return (LWGEOM *)lwcircstring_clone((LWCIRCSTRING *)lwgeom); case POLYGONTYPE: return (LWGEOM *)lwpoly_clone((LWPOLY *)lwgeom); case TRIANGLETYPE: return (LWGEOM *)lwtriangle_clone((LWTRIANGLE *)lwgeom); case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom); default: lwerror("lwgeom_clone: Unknown geometry type: %s", lwtype_name(lwgeom->type)); return NULL; } } /** * Deep-clone an #LWGEOM object. #POINTARRAY are copied. */ LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom) { LWDEBUGF(2, "lwgeom_clone called with %p, %s", lwgeom, lwtype_name(lwgeom->type)); switch (lwgeom->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case TRIANGLETYPE: return (LWGEOM *)lwline_clone_deep((LWLINE *)lwgeom); case POLYGONTYPE: return (LWGEOM *)lwpoly_clone_deep((LWPOLY *)lwgeom); case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_clone_deep((LWCOLLECTION *)lwgeom); default: lwerror("lwgeom_clone_deep: Unknown geometry type: %s", lwtype_name(lwgeom->type)); return NULL; } } /** * Return an alloced string */ char* lwgeom_to_ewkt(const LWGEOM *lwgeom) { char* wkt = NULL; size_t wkt_size = 0; wkt = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, 12, &wkt_size); if ( ! wkt ) { lwerror("Error writing geom %p to WKT", lwgeom); } return wkt; } /** * @brief geom1 same as geom2 * iff * + have same type * + have same # objects * + have same bvol * + each object in geom1 has a corresponding object in geom2 (see above) * @param lwgeom1 * @param lwgeom2 */ char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2) { LWDEBUGF(2, "lwgeom_same(%s, %s) called", lwtype_name(lwgeom1->type), lwtype_name(lwgeom2->type)); if ( lwgeom1->type != lwgeom2->type ) { LWDEBUG(3, " type differ"); return LW_FALSE; } if ( FLAGS_GET_ZM(lwgeom1->flags) != FLAGS_GET_ZM(lwgeom2->flags) ) { LWDEBUG(3, " ZM flags differ"); return LW_FALSE; } /* Check boxes if both already computed */ if ( lwgeom1->bbox && lwgeom2->bbox ) { /*lwnotice("bbox1:%p, bbox2:%p", lwgeom1->bbox, lwgeom2->bbox);*/ if ( ! gbox_same(lwgeom1->bbox, lwgeom2->bbox) ) { LWDEBUG(3, " bounding boxes differ"); return LW_FALSE; } } /* geoms have same type, invoke type-specific function */ switch (lwgeom1->type) { case POINTTYPE: return lwpoint_same((LWPOINT *)lwgeom1, (LWPOINT *)lwgeom2); case LINETYPE: return lwline_same((LWLINE *)lwgeom1, (LWLINE *)lwgeom2); case POLYGONTYPE: return lwpoly_same((LWPOLY *)lwgeom1, (LWPOLY *)lwgeom2); case TRIANGLETYPE: return lwtriangle_same((LWTRIANGLE *)lwgeom1, (LWTRIANGLE *)lwgeom2); case CIRCSTRINGTYPE: return lwcircstring_same((LWCIRCSTRING *)lwgeom1, (LWCIRCSTRING *)lwgeom2); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_same((LWCOLLECTION *)lwgeom1, (LWCOLLECTION *)lwgeom2); default: lwerror("lwgeom_same: unsupported geometry type: %s", lwtype_name(lwgeom1->type)); return LW_FALSE; } } int lwpoint_inside_circle(const LWPOINT *p, double cx, double cy, double rad) { const POINT2D *pt; POINT2D center; if ( ! p || ! p->point ) return LW_FALSE; pt = getPoint2d_cp(p->point, 0); center.x = cx; center.y = cy; if ( distance2d_pt_pt(pt, ¢er) < rad ) return LW_TRUE; return LW_FALSE; } void lwgeom_drop_bbox(LWGEOM *lwgeom) { if ( lwgeom->bbox ) lwfree(lwgeom->bbox); lwgeom->bbox = NULL; FLAGS_SET_BBOX(lwgeom->flags, 0); } /** * Ensure there's a box in the LWGEOM. * If the box is already there just return, * else compute it. */ void lwgeom_add_bbox(LWGEOM *lwgeom) { /* an empty LWGEOM has no bbox */ if ( lwgeom_is_empty(lwgeom) ) return; if ( lwgeom->bbox ) return; FLAGS_SET_BBOX(lwgeom->flags, 1); lwgeom->bbox = gbox_new(lwgeom->flags); lwgeom_calculate_gbox(lwgeom, lwgeom->bbox); } void lwgeom_refresh_bbox(LWGEOM *lwgeom) { lwgeom_drop_bbox(lwgeom); lwgeom_add_bbox(lwgeom); } void lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox) { if ( lwgeom_is_empty(lwgeom) ) return; FLAGS_SET_BBOX(lwgeom->flags, 1); if ( ! ( gbox || lwgeom->bbox ) ) { lwgeom->bbox = gbox_new(lwgeom->flags); lwgeom_calculate_gbox(lwgeom, lwgeom->bbox); } else if ( gbox && ! lwgeom->bbox ) { lwgeom->bbox = gbox_clone(gbox); } if ( lwgeom_is_collection(lwgeom) ) { uint32_t i; LWCOLLECTION *lwcol = (LWCOLLECTION*)lwgeom; for ( i = 0; i < lwcol->ngeoms; i++ ) { lwgeom_add_bbox_deep(lwcol->geoms[i], lwgeom->bbox); } } } const GBOX * lwgeom_get_bbox(const LWGEOM *lwg) { /* add it if not already there */ lwgeom_add_bbox((LWGEOM *)lwg); return lwg->bbox; } /** * Calculate the gbox for this geometry, a cartesian box or * geodetic box, depending on how it is flagged. */ int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox) { gbox->flags = lwgeom->flags; if( FLAGS_GET_GEODETIC(lwgeom->flags) ) return lwgeom_calculate_gbox_geodetic(lwgeom, gbox); else return lwgeom_calculate_gbox_cartesian(lwgeom, gbox); } void lwgeom_drop_srid(LWGEOM *lwgeom) { lwgeom->srid = SRID_UNKNOWN; /* TODO: To be changed to SRID_UNKNOWN */ } LWGEOM * lwgeom_segmentize2d(const LWGEOM *lwgeom, double dist) { switch (lwgeom->type) { case LINETYPE: return (LWGEOM *)lwline_segmentize2d((LWLINE *)lwgeom, dist); case POLYGONTYPE: return (LWGEOM *)lwpoly_segmentize2d((LWPOLY *)lwgeom, dist); case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_segmentize2d( (LWCOLLECTION *)lwgeom, dist); default: return lwgeom_clone(lwgeom); } } LWGEOM* lwgeom_force_2d(const LWGEOM *geom) { return lwgeom_force_dims(geom, 0, 0); } LWGEOM* lwgeom_force_3dz(const LWGEOM *geom) { return lwgeom_force_dims(geom, 1, 0); } LWGEOM* lwgeom_force_3dm(const LWGEOM *geom) { return lwgeom_force_dims(geom, 0, 1); } LWGEOM* lwgeom_force_4d(const LWGEOM *geom) { return lwgeom_force_dims(geom, 1, 1); } LWGEOM* lwgeom_force_dims(const LWGEOM *geom, int hasz, int hasm) { if (!geom) return NULL; switch(geom->type) { case POINTTYPE: return lwpoint_as_lwgeom(lwpoint_force_dims((LWPOINT*)geom, hasz, hasm)); case CIRCSTRINGTYPE: case LINETYPE: case TRIANGLETYPE: return lwline_as_lwgeom(lwline_force_dims((LWLINE*)geom, hasz, hasm)); case POLYGONTYPE: return lwpoly_as_lwgeom(lwpoly_force_dims((LWPOLY*)geom, hasz, hasm)); case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_as_lwgeom(lwcollection_force_dims((LWCOLLECTION*)geom, hasz, hasm)); default: lwerror("lwgeom_force_2d: unsupported geom type: %s", lwtype_name(geom->type)); return NULL; } } LWGEOM* lwgeom_force_sfs(LWGEOM *geom, int version) { LWCOLLECTION *col; uint32_t i; LWGEOM *g; /* SFS 1.2 version */ if (version == 120) { switch(geom->type) { /* SQL/MM types */ case CIRCSTRINGTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: return lwgeom_stroke(geom, 32); case COLLECTIONTYPE: col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version); return lwcollection_as_lwgeom((LWCOLLECTION*)geom); default: return (LWGEOM *)geom; } } /* SFS 1.1 version */ switch(geom->type) { /* SQL/MM types */ case CIRCSTRINGTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: return lwgeom_stroke(geom, 32); /* SFS 1.2 types */ case TRIANGLETYPE: g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)geom, 0, NULL)); lwgeom_free(geom); return g; case TINTYPE: col = (LWCOLLECTION*) geom; for ( i = 0; i < col->ngeoms; i++ ) { g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)col->geoms[i], 0, NULL)); lwgeom_free(col->geoms[i]); col->geoms[i] = g; } col->type = COLLECTIONTYPE; return lwmpoly_as_lwgeom((LWMPOLY*)geom); case POLYHEDRALSURFACETYPE: geom->type = COLLECTIONTYPE; return (LWGEOM *)geom; /* Collection */ case COLLECTIONTYPE: col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version); return lwcollection_as_lwgeom((LWCOLLECTION*)geom); default: return (LWGEOM *)geom; } } int32_t lwgeom_get_srid(const LWGEOM *geom) { if ( ! geom ) return SRID_UNKNOWN; return geom->srid; } int lwgeom_has_z(const LWGEOM *geom) { if ( ! geom ) return LW_FALSE; return FLAGS_GET_Z(geom->flags); } int lwgeom_has_m(const LWGEOM *geom) { if ( ! geom ) return LW_FALSE; return FLAGS_GET_M(geom->flags); } int lwgeom_is_solid(const LWGEOM *geom) { if ( ! geom ) return LW_FALSE; return FLAGS_GET_GEODETIC(geom->flags); } int lwgeom_ndims(const LWGEOM *geom) { if ( ! geom ) return 0; return FLAGS_NDIMS(geom->flags); } void lwgeom_set_geodetic(LWGEOM *geom, int value) { LWPOINT *pt; LWLINE *ln; LWPOLY *ply; LWCOLLECTION *col; uint32_t i; FLAGS_SET_GEODETIC(geom->flags, value); if ( geom->bbox ) FLAGS_SET_GEODETIC(geom->bbox->flags, value); switch(geom->type) { case POINTTYPE: pt = (LWPOINT*)geom; if ( pt->point ) FLAGS_SET_GEODETIC(pt->point->flags, value); break; case LINETYPE: ln = (LWLINE*)geom; if ( ln->points ) FLAGS_SET_GEODETIC(ln->points->flags, value); break; case POLYGONTYPE: ply = (LWPOLY*)geom; for ( i = 0; i < ply->nrings; i++ ) FLAGS_SET_GEODETIC(ply->rings[i]->flags, value); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) lwgeom_set_geodetic(col->geoms[i], value); break; default: lwerror("lwgeom_set_geodetic: unsupported geom type: %s", lwtype_name(geom->type)); return; } } void lwgeom_longitude_shift(LWGEOM *lwgeom) { uint32_t i; switch (lwgeom->type) { LWPOINT *point; LWLINE *line; LWPOLY *poly; LWTRIANGLE *triangle; LWCOLLECTION *coll; case POINTTYPE: point = (LWPOINT *)lwgeom; ptarray_longitude_shift(point->point); return; case LINETYPE: line = (LWLINE *)lwgeom; ptarray_longitude_shift(line->points); return; case POLYGONTYPE: poly = (LWPOLY *)lwgeom; for (i=0; inrings; i++) ptarray_longitude_shift(poly->rings[i]); return; case TRIANGLETYPE: triangle = (LWTRIANGLE *)lwgeom; ptarray_longitude_shift(triangle->points); return; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: coll = (LWCOLLECTION *)lwgeom; for (i=0; ingeoms; i++) lwgeom_longitude_shift(coll->geoms[i]); return; default: lwerror("lwgeom_longitude_shift: unsupported geom type: %s", lwtype_name(lwgeom->type)); } } int lwgeom_is_closed(const LWGEOM *geom) { int type = geom->type; if( lwgeom_is_empty(geom) ) return LW_FALSE; /* Test linear types for closure */ switch (type) { case LINETYPE: return lwline_is_closed((LWLINE*)geom); case POLYGONTYPE: return lwpoly_is_closed((LWPOLY*)geom); case CIRCSTRINGTYPE: return lwcircstring_is_closed((LWCIRCSTRING*)geom); case COMPOUNDTYPE: return lwcompound_is_closed((LWCOMPOUND*)geom); case TINTYPE: return lwtin_is_closed((LWTIN*)geom); case POLYHEDRALSURFACETYPE: return lwpsurface_is_closed((LWPSURFACE*)geom); } /* Recurse into collections and see if anything is not closed */ if ( lwgeom_is_collection(geom) ) { LWCOLLECTION *col = lwgeom_as_lwcollection(geom); uint32_t i; int closed; for ( i = 0; i < col->ngeoms; i++ ) { closed = lwgeom_is_closed(col->geoms[i]); if ( ! closed ) return LW_FALSE; } return LW_TRUE; } /* All non-linear non-collection types we will call closed */ return LW_TRUE; } int lwgeom_is_collection(const LWGEOM *geom) { if( ! geom ) return LW_FALSE; return lwtype_is_collection(geom->type); } /** Return TRUE if the geometry may contain sub-geometries, i.e. it is a MULTI* or COMPOUNDCURVE */ int lwtype_is_collection(uint8_t type) { switch (type) { case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: return LW_TRUE; break; default: return LW_FALSE; } } /** * Given an lwtype number, what homogeneous collection can hold it? */ uint32_t lwtype_get_collectiontype(uint8_t type) { switch (type) { case POINTTYPE: return MULTIPOINTTYPE; case LINETYPE: return MULTILINETYPE; case POLYGONTYPE: return MULTIPOLYGONTYPE; case CIRCSTRINGTYPE: return MULTICURVETYPE; case COMPOUNDTYPE: return MULTICURVETYPE; case CURVEPOLYTYPE: return MULTISURFACETYPE; case TRIANGLETYPE: return TINTYPE; default: return COLLECTIONTYPE; } } void lwgeom_free(LWGEOM *lwgeom) { /* There's nothing here to free... */ if( ! lwgeom ) return; LWDEBUGF(5,"freeing a %s",lwtype_name(lwgeom->type)); switch (lwgeom->type) { case POINTTYPE: lwpoint_free((LWPOINT *)lwgeom); break; case LINETYPE: lwline_free((LWLINE *)lwgeom); break; case POLYGONTYPE: lwpoly_free((LWPOLY *)lwgeom); break; case CIRCSTRINGTYPE: lwcircstring_free((LWCIRCSTRING *)lwgeom); break; case TRIANGLETYPE: lwtriangle_free((LWTRIANGLE *)lwgeom); break; case MULTIPOINTTYPE: lwmpoint_free((LWMPOINT *)lwgeom); break; case MULTILINETYPE: lwmline_free((LWMLINE *)lwgeom); break; case MULTIPOLYGONTYPE: lwmpoly_free((LWMPOLY *)lwgeom); break; case POLYHEDRALSURFACETYPE: lwpsurface_free((LWPSURFACE *)lwgeom); break; case TINTYPE: lwtin_free((LWTIN *)lwgeom); break; case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COLLECTIONTYPE: lwcollection_free((LWCOLLECTION *)lwgeom); break; default: lwerror("lwgeom_free called with unknown type (%d) %s", lwgeom->type, lwtype_name(lwgeom->type)); } return; } int lwgeom_needs_bbox(const LWGEOM *geom) { assert(geom); if ( geom->type == POINTTYPE ) { return LW_FALSE; } else if ( geom->type == LINETYPE ) { if ( lwgeom_count_vertices(geom) <= 2 ) return LW_FALSE; else return LW_TRUE; } else if ( geom->type == MULTIPOINTTYPE ) { if ( ((LWCOLLECTION*)geom)->ngeoms == 1 ) return LW_FALSE; else return LW_TRUE; } else if ( geom->type == MULTILINETYPE ) { if ( ((LWCOLLECTION*)geom)->ngeoms == 1 && lwgeom_count_vertices(geom) <= 2 ) return LW_FALSE; else return LW_TRUE; } else { return LW_TRUE; } } /** * Count points in an #LWGEOM. * TODO: Make sure the internal functions don't overflow */ uint32_t lwgeom_count_vertices(const LWGEOM *geom) { int result = 0; /* Null? Zero. */ if( ! geom ) return 0; LWDEBUGF(4, "lwgeom_count_vertices got type %s", lwtype_name(geom->type)); /* Empty? Zero. */ if( lwgeom_is_empty(geom) ) return 0; switch (geom->type) { case POINTTYPE: result = 1; break; case TRIANGLETYPE: case CIRCSTRINGTYPE: case LINETYPE: result = lwline_count_vertices((LWLINE *)geom); break; case POLYGONTYPE: result = lwpoly_count_vertices((LWPOLY *)geom); break; case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: result = lwcollection_count_vertices((LWCOLLECTION *)geom); break; default: lwerror("%s: unsupported input geometry type: %s", __func__, lwtype_name(geom->type)); break; } LWDEBUGF(3, "counted %d vertices", result); return result; } /** * For an #LWGEOM, returns 0 for points, 1 for lines, * 2 for polygons, 3 for volume, and the max dimension * of a collection. */ int lwgeom_dimension(const LWGEOM *geom) { /* Null? Zero. */ if( ! geom ) return -1; LWDEBUGF(4, "lwgeom_dimension got type %s", lwtype_name(geom->type)); /* Empty? Zero. */ /* if( lwgeom_is_empty(geom) ) return 0; */ switch (geom->type) { case POINTTYPE: case MULTIPOINTTYPE: return 0; case CIRCSTRINGTYPE: case LINETYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTILINETYPE: return 1; case TRIANGLETYPE: case POLYGONTYPE: case CURVEPOLYTYPE: case MULTISURFACETYPE: case MULTIPOLYGONTYPE: case TINTYPE: return 2; case POLYHEDRALSURFACETYPE: { /* A closed polyhedral surface contains a volume. */ int closed = lwpsurface_is_closed((LWPSURFACE*)geom); return ( closed ? 3 : 2 ); } case COLLECTIONTYPE: { int maxdim = 0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for( i = 0; i < col->ngeoms; i++ ) { int dim = lwgeom_dimension(col->geoms[i]); maxdim = ( dim > maxdim ? dim : maxdim ); } return maxdim; } default: lwerror("%s: unsupported input geometry type: %s", __func__, lwtype_name(geom->type)); } return -1; } /** * Count rings in an #LWGEOM. */ uint32_t lwgeom_count_rings(const LWGEOM *geom) { int result = 0; /* Null? Empty? Zero. */ if( ! geom || lwgeom_is_empty(geom) ) return 0; switch (geom->type) { case POINTTYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case LINETYPE: result = 0; break; case TRIANGLETYPE: result = 1; break; case POLYGONTYPE: result = ((LWPOLY *)geom)->nrings; break; case CURVEPOLYTYPE: result = ((LWCURVEPOLY *)geom)->nrings; break; case MULTISURFACETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: { LWCOLLECTION *col = (LWCOLLECTION*)geom; uint32_t i = 0; for( i = 0; i < col->ngeoms; i++ ) result += lwgeom_count_rings(col->geoms[i]); break; } default: lwerror("lwgeom_count_rings: unsupported input geometry type: %s", lwtype_name(geom->type)); break; } LWDEBUGF(3, "counted %d rings", result); return result; } int lwgeom_has_srid(const LWGEOM *geom) { if ( geom->srid != SRID_UNKNOWN ) return LW_TRUE; return LW_FALSE; } static int lwcollection_dimensionality(const LWCOLLECTION *col) { uint32_t i; int dimensionality = 0; for ( i = 0; i < col->ngeoms; i++ ) { int d = lwgeom_dimensionality(col->geoms[i]); if ( d > dimensionality ) dimensionality = d; } return dimensionality; } extern int lwgeom_dimensionality(const LWGEOM *geom) { int dim; LWDEBUGF(3, "lwgeom_dimensionality got type %s", lwtype_name(geom->type)); switch (geom->type) { case POINTTYPE: case MULTIPOINTTYPE: return 0; break; case LINETYPE: case CIRCSTRINGTYPE: case MULTILINETYPE: case COMPOUNDTYPE: case MULTICURVETYPE: return 1; break; case POLYGONTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: return 2; break; case POLYHEDRALSURFACETYPE: case TINTYPE: dim = lwgeom_is_closed(geom)?3:2; return dim; break; case COLLECTIONTYPE: return lwcollection_dimensionality((const LWCOLLECTION *)geom); break; default: lwerror("lwgeom_dimensionality: unsupported input geometry type: %s", lwtype_name(geom->type)); break; } return 0; } extern LWGEOM* lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance) { LWGEOM *out = lwgeom_clone_deep(in); lwgeom_remove_repeated_points_in_place(out, tolerance); return out; } void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2) { LWCOLLECTION *col; LWPOLY *poly; uint32_t i; if ( (!in) || lwgeom_is_empty(in) ) return; /* TODO: check for lwgeom NOT having the specified dimension ? */ LWDEBUGF(4, "lwgeom_flip_coordinates, got type: %s", lwtype_name(in->type)); switch (in->type) { case POINTTYPE: ptarray_swap_ordinates(lwgeom_as_lwpoint(in)->point, o1, o2); break; case LINETYPE: ptarray_swap_ordinates(lwgeom_as_lwline(in)->points, o1, o2); break; case CIRCSTRINGTYPE: ptarray_swap_ordinates(lwgeom_as_lwcircstring(in)->points, o1, o2); break; case POLYGONTYPE: poly = (LWPOLY *) in; for (i=0; inrings; i++) { ptarray_swap_ordinates(poly->rings[i], o1, o2); } break; case TRIANGLETYPE: ptarray_swap_ordinates(lwgeom_as_lwtriangle(in)->points, o1, o2); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTISURFACETYPE: case MULTICURVETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: col = (LWCOLLECTION *) in; for (i=0; ingeoms; i++) { lwgeom_swap_ordinates(col->geoms[i], o1, o2); } break; default: lwerror("lwgeom_swap_ordinates: unsupported geometry type: %s", lwtype_name(in->type)); return; } /* only refresh bbox if X or Y changed */ if ( in->bbox && (o1 < 2 || o2 < 2) ) { lwgeom_refresh_bbox(in); } } void lwgeom_set_srid(LWGEOM *geom, int32_t srid) { uint32_t i; LWDEBUGF(4,"entered with srid=%d",srid); geom->srid = srid; if ( lwgeom_is_collection(geom) ) { /* All the children are set to the same SRID value */ LWCOLLECTION *col = lwgeom_as_lwcollection(geom); for ( i = 0; i < col->ngeoms; i++ ) { lwgeom_set_srid(col->geoms[i], srid); } } } /**************************************************************/ int lwgeom_remove_repeated_points_in_place(LWGEOM *geom, double tolerance) { int geometry_modified = LW_FALSE; switch (geom->type) { /* No-op! Cannot remote points */ case POINTTYPE: case TRIANGLETYPE: return geometry_modified; case LINETYPE: { LWLINE *g = (LWLINE*)(geom); POINTARRAY *pa = g->points; uint32_t npoints = pa->npoints; ptarray_remove_repeated_points_in_place(pa, tolerance, 2); geometry_modified = npoints != pa->npoints; /* Invalid output */ if (pa->npoints == 1 && pa->maxpoints > 1) { /* Use first point as last point */ pa->npoints = 2; ptarray_copy_point(pa, 0, 1); } break; } case POLYGONTYPE: { uint32_t i, j = 0; LWPOLY *g = (LWPOLY*)(geom); for (i = 0; i < g->nrings; i++) { POINTARRAY *pa = g->rings[i]; int minpoints = 4; uint32_t npoints = 0; /* Skip zero'ed out rings */ if(!pa) continue; npoints = pa->npoints; ptarray_remove_repeated_points_in_place(pa, tolerance, minpoints); geometry_modified |= npoints != pa->npoints; /* Drop collapsed rings */ if(pa->npoints < 4) { geometry_modified = LW_TRUE; ptarray_free(pa); continue; } g->rings[j++] = pa; } /* Update ring count */ g->nrings = j; break; } case MULTIPOINTTYPE: { static uint32_t out_stack_size = 32; double tolsq = tolerance*tolerance; uint32_t i, j, n = 0; LWMPOINT *mpt = (LWMPOINT *)(geom); LWPOINT **out; LWPOINT *out_stack[out_stack_size]; int use_heap = (mpt->ngeoms > out_stack_size); /* No-op on empty */ if (mpt->ngeoms < 2) return geometry_modified; /* We cannot write directly back to the multipoint */ /* geoms array because we're reading out of it still */ /* so we use a side array */ if (use_heap) out = lwalloc(sizeof(LWMPOINT *) * mpt->ngeoms); else out = out_stack; /* Inefficient O(n^2) implementation */ for (i = 0; i < mpt->ngeoms; i++) { int seen = 0; LWPOINT *p1 = mpt->geoms[i]; const POINT2D *pt1 = getPoint2d_cp(p1->point, 0); for (j = 0; j < n; j++) { LWPOINT *p2 = out[j]; const POINT2D *pt2 = getPoint2d_cp(p2->point, 0); if (distance2d_sqr_pt_pt(pt1, pt2) <= tolsq) { seen = 1; break; } } if (seen) { lwpoint_free(p1); continue; } out[n++] = p1; } /* Copy remaining points back into the input */ /* array */ memcpy(mpt->geoms, out, sizeof(LWPOINT *) * n); geometry_modified = mpt->ngeoms != n; mpt->ngeoms = n; if (use_heap) lwfree(out); break; } case CIRCSTRINGTYPE: /* Dunno how to handle these, will return untouched */ return geometry_modified; /* Can process most multi* types as generic collection */ case MULTILINETYPE: case MULTIPOLYGONTYPE: case TINTYPE: case COLLECTIONTYPE: /* Curve types we mostly ignore, but allow the linear */ /* portions to be processed by recursing into them */ case MULTICURVETYPE: case CURVEPOLYTYPE: case MULTISURFACETYPE: case COMPOUNDTYPE: { uint32_t i, j = 0; LWCOLLECTION *col = (LWCOLLECTION*)(geom); for (i = 0; i < col->ngeoms; i++) { LWGEOM *g = col->geoms[i]; if (!g) continue; geometry_modified |= lwgeom_remove_repeated_points_in_place(g, tolerance); /* Drop zero'ed out geometries */ if(lwgeom_is_empty(g)) { lwgeom_free(g); continue; } col->geoms[j++] = g; } /* Update geometry count */ col->ngeoms = j; break; } default: { lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type)); break; } } if (geometry_modified) { lwgeom_drop_bbox(geom); } return geometry_modified; } /**************************************************************/ int lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed) { int modified = LW_FALSE; switch (geom->type) { /* No-op! Cannot simplify points or triangles */ case POINTTYPE: return modified; case TRIANGLETYPE: { if (preserve_collapsed) return modified; LWTRIANGLE *t = lwgeom_as_lwtriangle(geom); POINTARRAY *pa = t->points; ptarray_simplify_in_place(pa, epsilon, 0); if (pa->npoints < 3) { pa->npoints = 0; modified = LW_TRUE; } break; } case LINETYPE: { LWLINE *g = (LWLINE*)(geom); POINTARRAY *pa = g->points; uint32_t in_npoints = pa->npoints; ptarray_simplify_in_place(pa, epsilon, 2); modified = in_npoints != pa->npoints; /* Invalid output */ if (pa->npoints == 1 && pa->maxpoints > 1) { /* Use first point as last point */ if (preserve_collapsed) { pa->npoints = 2; ptarray_copy_point(pa, 0, 1); } /* Finish the collapse process */ else { pa->npoints = 0; } } /* Duped output, force collapse */ if (pa->npoints == 2 && !preserve_collapsed) { if (p2d_same(getPoint2d_cp(pa, 0), getPoint2d_cp(pa, 1))) pa->npoints = 0; } break; } case POLYGONTYPE: { uint32_t i, j = 0; LWPOLY *g = (LWPOLY*)(geom); for (i = 0; i < g->nrings; i++) { POINTARRAY *pa = g->rings[i]; /* Only stop collapse on first ring */ int minpoints = (preserve_collapsed && i == 0) ? 4 : 0; /* Skip zero'ed out rings */ if(!pa) continue; uint32_t in_npoints = pa->npoints; ptarray_simplify_in_place(pa, epsilon, minpoints); modified |= in_npoints != pa->npoints; /* Drop collapsed rings */ if(pa->npoints < 4) { /* Any ring deeper than this one is, by OGR definition, smaller * so we drop them all */ for (; i < g->nrings; i++) { pa = g->rings[i]; ptarray_free(pa); } break; } g->rings[j++] = pa; } /* Update ring count */ g->nrings = j; break; } /* Can process all multi* types as generic collection */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case TINTYPE: case COLLECTIONTYPE: { uint32_t i, j = 0; LWCOLLECTION *col = (LWCOLLECTION*)geom; for (i = 0; i < col->ngeoms; i++) { LWGEOM *g = col->geoms[i]; if (!g) continue; modified |= lwgeom_simplify_in_place(g, epsilon, preserve_collapsed); /* Drop zero'ed out geometries */ if(lwgeom_is_empty(g)) { lwgeom_free(g); continue; } col->geoms[j++] = g; } /* Update geometry count */ col->ngeoms = j; break; } default: { lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type)); break; } } if (modified) { lwgeom_drop_bbox(geom); } return modified; } LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed) { LWGEOM *lwgeom_out = lwgeom_clone_deep(igeom); lwgeom_simplify_in_place(lwgeom_out, dist, preserve_collapsed); if (lwgeom_is_empty(lwgeom_out)) { lwgeom_free(lwgeom_out); return NULL; } return lwgeom_out; } /**************************************************************/ double lwgeom_area(const LWGEOM *geom) { int type = geom->type; if ( type == POLYGONTYPE ) return lwpoly_area((LWPOLY*)geom); else if ( type == CURVEPOLYTYPE ) return lwcurvepoly_area((LWCURVEPOLY*)geom); else if (type == TRIANGLETYPE ) return lwtriangle_area((LWTRIANGLE*)geom); else if ( lwgeom_is_collection(geom) ) { double area = 0.0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) area += lwgeom_area(col->geoms[i]); return area; } else return 0.0; } double lwgeom_perimeter(const LWGEOM *geom) { int type = geom->type; if ( type == POLYGONTYPE ) return lwpoly_perimeter((LWPOLY*)geom); else if ( type == CURVEPOLYTYPE ) return lwcurvepoly_perimeter((LWCURVEPOLY*)geom); else if ( type == TRIANGLETYPE ) return lwtriangle_perimeter((LWTRIANGLE*)geom); else if ( lwgeom_is_collection(geom) ) { double perimeter = 0.0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) perimeter += lwgeom_perimeter(col->geoms[i]); return perimeter; } else return 0.0; } double lwgeom_perimeter_2d(const LWGEOM *geom) { int type = geom->type; if ( type == POLYGONTYPE ) return lwpoly_perimeter_2d((LWPOLY*)geom); else if ( type == CURVEPOLYTYPE ) return lwcurvepoly_perimeter_2d((LWCURVEPOLY*)geom); else if ( type == TRIANGLETYPE ) return lwtriangle_perimeter_2d((LWTRIANGLE*)geom); else if ( lwgeom_is_collection(geom) ) { double perimeter = 0.0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) perimeter += lwgeom_perimeter_2d(col->geoms[i]); return perimeter; } else return 0.0; } double lwgeom_length(const LWGEOM *geom) { int type = geom->type; if ( type == LINETYPE ) return lwline_length((LWLINE*)geom); else if ( type == CIRCSTRINGTYPE ) return lwcircstring_length((LWCIRCSTRING*)geom); else if ( type == COMPOUNDTYPE ) return lwcompound_length((LWCOMPOUND*)geom); else if ( lwgeom_is_collection(geom) ) { double length = 0.0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) length += lwgeom_length(col->geoms[i]); return length; } else return 0.0; } double lwgeom_length_2d(const LWGEOM *geom) { int type = geom->type; if ( type == LINETYPE ) return lwline_length_2d((LWLINE*)geom); else if ( type == CIRCSTRINGTYPE ) return lwcircstring_length_2d((LWCIRCSTRING*)geom); else if ( type == COMPOUNDTYPE ) return lwcompound_length_2d((LWCOMPOUND*)geom); else if ( lwgeom_is_collection(geom) ) { double length = 0.0; uint32_t i; LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) length += lwgeom_length_2d(col->geoms[i]); return length; } else return 0.0; } void lwgeom_affine(LWGEOM *geom, const AFFINE *affine) { int type = geom->type; uint32_t i; switch(type) { /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */ case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case TRIANGLETYPE: { LWLINE *l = (LWLINE*)geom; ptarray_affine(l->points, affine); break; } case POLYGONTYPE: { LWPOLY *p = (LWPOLY*)geom; for( i = 0; i < p->nrings; i++ ) ptarray_affine(p->rings[i], affine); break; } case CURVEPOLYTYPE: { LWCURVEPOLY *c = (LWCURVEPOLY*)geom; for( i = 0; i < c->nrings; i++ ) lwgeom_affine(c->rings[i], affine); break; } default: { if( lwgeom_is_collection(geom) ) { LWCOLLECTION *c = (LWCOLLECTION*)geom; for( i = 0; i < c->ngeoms; i++ ) { lwgeom_affine(c->geoms[i], affine); } } else { lwerror("lwgeom_affine: unable to handle type '%s'", lwtype_name(type)); } } } } void lwgeom_scale(LWGEOM *geom, const POINT4D *factor) { int type = geom->type; uint32_t i; switch(type) { /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */ case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case TRIANGLETYPE: { LWLINE *l = (LWLINE*)geom; ptarray_scale(l->points, factor); break; } case POLYGONTYPE: { LWPOLY *p = (LWPOLY*)geom; for( i = 0; i < p->nrings; i++ ) ptarray_scale(p->rings[i], factor); break; } case CURVEPOLYTYPE: { LWCURVEPOLY *c = (LWCURVEPOLY*)geom; for( i = 0; i < c->nrings; i++ ) lwgeom_scale(c->rings[i], factor); break; } default: { if( lwgeom_is_collection(geom) ) { LWCOLLECTION *c = (LWCOLLECTION*)geom; for( i = 0; i < c->ngeoms; i++ ) { lwgeom_scale(c->geoms[i], factor); } } else { lwerror("lwgeom_scale: unable to handle type '%s'", lwtype_name(type)); } } } /* Recompute bbox if needed */ if ( geom->bbox ) { /* TODO: expose a gbox_scale function */ geom->bbox->xmin *= factor->x; geom->bbox->xmax *= factor->x; geom->bbox->ymin *= factor->y; geom->bbox->ymax *= factor->y; geom->bbox->zmin *= factor->z; geom->bbox->zmax *= factor->z; geom->bbox->mmin *= factor->m; geom->bbox->mmax *= factor->m; } } LWGEOM * lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm) { switch(type) { case POINTTYPE: return lwpoint_as_lwgeom(lwpoint_construct_empty(srid, hasz, hasm)); case LINETYPE: return lwline_as_lwgeom(lwline_construct_empty(srid, hasz, hasm)); case POLYGONTYPE: return lwpoly_as_lwgeom(lwpoly_construct_empty(srid, hasz, hasm)); case CURVEPOLYTYPE: return lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(srid, hasz, hasm)); case CIRCSTRINGTYPE: return lwcircstring_as_lwgeom(lwcircstring_construct_empty(srid, hasz, hasm)); case TRIANGLETYPE: return lwtriangle_as_lwgeom(lwtriangle_construct_empty(srid, hasz, hasm)); case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return lwcollection_as_lwgeom(lwcollection_construct_empty(type, srid, hasz, hasm)); default: lwerror("lwgeom_construct_empty: unsupported geometry type: %s", lwtype_name(type)); return NULL; } } int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt) { if ( ! lwgeom ) return LW_FAILURE; switch( lwgeom->type ) { case POINTTYPE: return ptarray_startpoint(((LWPOINT*)lwgeom)->point, pt); case TRIANGLETYPE: case CIRCSTRINGTYPE: case LINETYPE: return ptarray_startpoint(((LWLINE*)lwgeom)->points, pt); case POLYGONTYPE: return lwpoly_startpoint((LWPOLY*)lwgeom, pt); case TINTYPE: case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case POLYHEDRALSURFACETYPE: return lwcollection_startpoint((LWCOLLECTION*)lwgeom, pt); default: lwerror("lwgeom_startpoint: unsupported geometry type: %s", lwtype_name(lwgeom->type)); return LW_FAILURE; } } void lwgeom_grid_in_place(LWGEOM *geom, const gridspec *grid) { if (!geom) return; switch ( geom->type ) { case POINTTYPE: { LWPOINT *pt = (LWPOINT*)(geom); ptarray_grid_in_place(pt->point, grid); return; } case CIRCSTRINGTYPE: case TRIANGLETYPE: case LINETYPE: { LWLINE *ln = (LWLINE*)(geom); ptarray_grid_in_place(ln->points, grid); /* For invalid line, return an EMPTY */ if (ln->points->npoints < 2) ln->points->npoints = 0; return; } case POLYGONTYPE: { LWPOLY *ply = (LWPOLY*)(geom); if (!ply->rings) return; /* Check first the external ring */ uint32_t i = 0; POINTARRAY *pa = ply->rings[0]; ptarray_grid_in_place(pa, grid); if (pa->npoints < 4) { /* External ring collapsed: free everything */ for (i = 0; i < ply->nrings; i++) { ptarray_free(ply->rings[i]); } ply->nrings = 0; return; } /* Check the other rings */ uint32_t j = 1; for (i = 1; i < ply->nrings; i++) { POINTARRAY *pa = ply->rings[i]; ptarray_grid_in_place(pa, grid); /* Skip bad rings */ if (pa->npoints >= 4) { ply->rings[j++] = pa; } else { ptarray_free(pa); } } /* Adjust ring count appropriately */ ply->nrings = j; return; } case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case TINTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: { LWCOLLECTION *col = (LWCOLLECTION*)(geom); uint32_t i, j = 0; if (!col->geoms) return; for (i = 0; i < col->ngeoms; i++) { LWGEOM *g = col->geoms[i]; lwgeom_grid_in_place(g, grid); /* Empty geoms need to be freed */ /* before we move on */ if (lwgeom_is_empty(g)) { lwgeom_free(g); continue; } col->geoms[j++] = g; } col->ngeoms = j; return; } default: { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); return; } } } LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid) { LWGEOM *lwgeom_out = lwgeom_clone_deep(lwgeom); lwgeom_grid_in_place(lwgeom_out, grid); return lwgeom_out; } /* Prototype for recursion */ static void lwgeom_subdivide_recursive(const LWGEOM *geom, uint8_t dimension, uint32_t maxvertices, uint32_t depth, LWCOLLECTION *col); static void lwgeom_subdivide_recursive(const LWGEOM *geom, uint8_t dimension, uint32_t maxvertices, uint32_t depth, LWCOLLECTION *col) { const uint32_t maxdepth = 50; if (!geom) return; const GBOX *box_in = lwgeom_get_bbox(geom); if (!box_in) return; GBOX clip; gbox_duplicate(box_in, &clip); double width = clip.xmax - clip.xmin; double height = clip.ymax - clip.ymin; if ( geom->type == POLYHEDRALSURFACETYPE || geom->type == TINTYPE ) lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(geom->type)); if ( width == 0.0 && height == 0.0 ) { if ( geom->type == POINTTYPE && dimension == 0) lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom)); return; } if (width == 0.0) { clip.xmax += FP_TOLERANCE; clip.xmin -= FP_TOLERANCE; width = 2 * FP_TOLERANCE; } if (height == 0.0) { clip.ymax += FP_TOLERANCE; clip.ymin -= FP_TOLERANCE; height = 2 * FP_TOLERANCE; } /* Always just recurse into collections */ if ( lwgeom_is_collection(geom) && geom->type != MULTIPOINTTYPE ) { LWCOLLECTION *incol = (LWCOLLECTION*)geom; /* Don't increment depth yet, since we aren't actually * subdividing geometries yet */ for (uint32_t i = 0; i < incol->ngeoms; i++ ) lwgeom_subdivide_recursive(incol->geoms[i], dimension, maxvertices, depth, col); return; } if (lwgeom_dimension(geom) < dimension) { /* We've hit a lower dimension object produced by clipping at * a shallower recursion level. Ignore it. */ return; } /* But don't go too far. 2^50 ~= 10^15, that's enough subdivision */ /* Just add what's left */ if ( depth > maxdepth ) { lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom)); return; } uint32_t nvertices = lwgeom_count_vertices(geom); /* Skip empties entirely */ if (nvertices == 0) return; /* If it is under the vertex tolerance, just add it, we're done */ if (nvertices <= maxvertices) { lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom)); return; } uint8_t split_ordinate = (width > height) ? 0 : 1; double center = (split_ordinate == 0) ? (clip.xmin + clip.xmax) / 2 : (clip.ymin + clip.ymax) / 2; double pivot = DBL_MAX; if (geom->type == POLYGONTYPE) { uint32_t ring_to_trim = 0; double ring_area = 0; double pivot_eps = DBL_MAX; double pt_eps = DBL_MAX; POINTARRAY *pa; LWPOLY *lwpoly = (LWPOLY *)geom; /* if there are more points in holes than in outer ring */ if (nvertices >= 2 * lwpoly->rings[0]->npoints) { /* trim holes starting from biggest */ for (uint32_t i = 1; i < lwpoly->nrings; i++) { double current_ring_area = fabs(ptarray_signed_area(lwpoly->rings[i])); if (current_ring_area >= ring_area) { ring_area = current_ring_area; ring_to_trim = i; } } } pa = lwpoly->rings[ring_to_trim]; /* find most central point in chosen ring */ for (uint32_t i = 0; i < pa->npoints; i++) { double pt; if (split_ordinate == 0) pt = getPoint2d_cp(pa, i)->x; else pt = getPoint2d_cp(pa, i)->y; pt_eps = fabs(pt - center); if (pivot_eps > pt_eps) { pivot = pt; pivot_eps = pt_eps; } } } GBOX subbox1, subbox2; gbox_duplicate(&clip, &subbox1); gbox_duplicate(&clip, &subbox2); if (pivot == DBL_MAX) pivot = center; if (split_ordinate == 0) { if (FP_NEQUALS(subbox1.xmax, pivot) && FP_NEQUALS(subbox1.xmin, pivot)) subbox1.xmax = subbox2.xmin = pivot; else subbox1.xmax = subbox2.xmin = center; } else { if (FP_NEQUALS(subbox1.ymax, pivot) && FP_NEQUALS(subbox1.ymin, pivot)) subbox1.ymax = subbox2.ymin = pivot; else subbox1.ymax = subbox2.ymin = center; } ++depth; { LWGEOM *subbox = (LWGEOM *)lwpoly_construct_envelope( geom->srid, subbox1.xmin, subbox1.ymin, subbox1.xmax, subbox1.ymax); LWGEOM *clipped = lwgeom_intersection(geom, subbox); lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE); lwgeom_free(subbox); if (clipped && !lwgeom_is_empty(clipped)) { lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col); lwgeom_free(clipped); } } { LWGEOM *subbox = (LWGEOM *)lwpoly_construct_envelope( geom->srid, subbox2.xmin, subbox2.ymin, subbox2.xmax, subbox2.ymax); LWGEOM *clipped = lwgeom_intersection(geom, subbox); lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE); lwgeom_free(subbox); if (clipped && !lwgeom_is_empty(clipped)) { lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col); lwgeom_free(clipped); } } } LWCOLLECTION * lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices) { static uint32_t startdepth = 0; static uint32_t minmaxvertices = 5; LWCOLLECTION *col; col = lwcollection_construct_empty(COLLECTIONTYPE, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom)); if ( lwgeom_is_empty(geom) ) return col; if ( maxvertices < minmaxvertices ) { lwcollection_free(col); lwerror("%s: cannot subdivide to fewer than %d vertices per output", __func__, minmaxvertices); } lwgeom_subdivide_recursive(geom, lwgeom_dimension(geom), maxvertices, startdepth, col); lwgeom_set_srid((LWGEOM*)col, geom->srid); return col; } int lwgeom_is_trajectory(const LWGEOM *geom) { int type = geom->type; if( type != LINETYPE ) { lwnotice("Geometry is not a LINESTRING"); return LW_FALSE; } return lwline_is_trajectory((LWLINE*)geom); } static uint8_t bits_for_precision(int32_t significant_digits) { int32_t bits_needed = ceil(significant_digits / log10(2)); if (bits_needed > 52) { return 52; } else if (bits_needed < 1) { return 1; } return bits_needed; } static double trim_preserve_decimal_digits(double d, int32_t decimal_digits) { if (d == 0) return 0; int digits_left_of_decimal = (int) (1 + log10(fabs(d))); uint8_t bits_needed = bits_for_precision(decimal_digits + digits_left_of_decimal); uint64_t mask = 0xffffffffffffffffULL << (52 - bits_needed); uint64_t dint = 0; size_t dsz = sizeof(d) < sizeof(dint) ? sizeof(d) : sizeof(dint); memcpy(&dint, &d, dsz); dint &= mask; memcpy(&d, &dint, dsz); return d; } void lwgeom_trim_bits_in_place(LWGEOM* geom, int32_t prec_x, int32_t prec_y, int32_t prec_z, int32_t prec_m) { LWPOINTITERATOR* it = lwpointiterator_create_rw(geom); POINT4D p; while (lwpointiterator_has_next(it)) { lwpointiterator_peek(it, &p); p.x = trim_preserve_decimal_digits(p.x, prec_x); p.y = trim_preserve_decimal_digits(p.y, prec_y); if (lwgeom_has_z(geom)) p.z = trim_preserve_decimal_digits(p.z, prec_z); if (lwgeom_has_m(geom)) p.m = trim_preserve_decimal_digits(p.m, prec_m); lwpointiterator_modify_next(it, &p); } lwpointiterator_destroy(it); } lwgeom/src/liblwgeom/lwgeom_geos.c0000644000176200001440000013265614332732632017011 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011-2014 Sandro Santilli * Copyright 2015-2018 Daniel Baston * Copyright 2017-2018 Darafei Praliaskouski * **********************************************************************/ #include "lwgeom_geos.h" #include "liblwgeom.h" #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "lwrandom.h" #include #include LWTIN* lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d); #define AUTOFIX LW_TRUE #define LWGEOM_GEOS_ERRMSG_MAXSIZE 256 char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]; extern void lwgeom_geos_error(const char* fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ if (LWGEOM_GEOS_ERRMSG_MAXSIZE - 1 < vsnprintf(lwgeom_geos_errmsg, LWGEOM_GEOS_ERRMSG_MAXSIZE - 1, fmt, ap)) lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE - 1] = '\0'; va_end(ap); } /* Destroy any non-null GEOSGeometry* pointers passed as arguments */ #define GEOS_FREE(...) \ do { \ geos_destroy((sizeof((void*[]){__VA_ARGS__})/sizeof(void*)), __VA_ARGS__); \ } while (0) /* Pass the latest GEOS error to lwerror, then return NULL */ #define GEOS_FAIL() \ do { \ lwerror("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \ return NULL; \ } while (0) /* Pass the latest GEOS error to lwdebug, then return NULL */ #define GEOS_FAIL_DEBUG() \ do \ { \ lwdebug(1, "%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \ return NULL; \ } while (0) #define GEOS_FREE_AND_FAIL(...) \ do { \ GEOS_FREE(__VA_ARGS__); \ GEOS_FAIL(); \ } while (0) #define GEOS_FREE_AND_FAIL_DEBUG(...) \ do \ { \ GEOS_FREE(__VA_ARGS__); \ GEOS_FAIL_DEBUG(); \ } while (0) /* Return the consistent SRID of all inputs, or call lwerror * in case of SRID mismatch. */ #define RESULT_SRID(...) \ (get_result_srid((sizeof((const void*[]){__VA_ARGS__})/sizeof(void*)), __func__, __VA_ARGS__)) /* Free any non-null GEOSGeometry* pointers passed as arguments * * Called by GEOS_FREE, which populates 'count' */ static void geos_destroy(size_t count, ...) { va_list ap; va_start(ap, count); while (count--) { GEOSGeometry* g = va_arg(ap, GEOSGeometry*); if (g) { GEOSGeom_destroy(g); } } } /* ** GEOS <==> PostGIS conversion functions ** ** Default conversion creates a GEOS point array, then iterates through the ** PostGIS points, setting each value in the GEOS array one at a time. ** */ /* Return a POINTARRAY from a GEOSCoordSeq */ POINTARRAY* ptarray_from_GEOSCoordSeq(const GEOSCoordSequence* cs, uint8_t want3d) { uint32_t dims = 2; uint32_t size = 0, i; POINTARRAY* pa; POINT4D point = { 0.0, 0.0, 0.0, 0.0 }; LWDEBUG(2, "ptarray_fromGEOSCoordSeq called"); if (!GEOSCoordSeq_getSize(cs, &size)) lwerror("Exception thrown"); LWDEBUGF(4, " GEOSCoordSeq size: %d", size); if (want3d) { if (!GEOSCoordSeq_getDimensions(cs, &dims)) lwerror("Exception thrown"); LWDEBUGF(4, " GEOSCoordSeq dimensions: %d", dims); /* forget higher dimensions (if any) */ if (dims > 3) dims = 3; } LWDEBUGF(4, " output dimensions: %d", dims); pa = ptarray_construct((dims == 3), 0, size); for (i = 0; i < size; i++) { #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_getX(cs, i, &(point.x)); GEOSCoordSeq_getY(cs, i, &(point.y)); if (dims >= 3) GEOSCoordSeq_getZ(cs, i, &(point.z)); #else if (dims >= 3) GEOSCoordSeq_getXYZ(cs, i, &(point.x), &(point.y), &(point.z)); else GEOSCoordSeq_getXY(cs, i, &(point.x), &(point.y)); #endif ptarray_set_point4d(pa, i, &point); } return pa; } /* Return an LWGEOM from a Geometry */ LWGEOM* GEOS2LWGEOM(const GEOSGeometry* geom, uint8_t want3d) { int type = GEOSGeomTypeId(geom); int SRID = GEOSGetSRID(geom); /* GEOS's 0 is equivalent to our unknown as for SRID values */ if (SRID == 0) SRID = SRID_UNKNOWN; if (want3d && !GEOSHasZ(geom)) { LWDEBUG(3, "Geometry has no Z, won't provide one"); want3d = 0; } switch (type) { const GEOSCoordSequence* cs; POINTARRAY *pa, **ppaa; const GEOSGeometry* g; LWGEOM** geoms; uint32_t i, ngeoms; case GEOS_POINT: LWDEBUG(4, "lwgeom_from_geometry: it's a Point"); cs = GEOSGeom_getCoordSeq(geom); if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoint_construct_empty(SRID, want3d, 0); pa = ptarray_from_GEOSCoordSeq(cs, want3d); return (LWGEOM*)lwpoint_construct(SRID, NULL, pa); case GEOS_LINESTRING: case GEOS_LINEARRING: LWDEBUG(4, "lwgeom_from_geometry: it's a LineString or LinearRing"); if (GEOSisEmpty(geom)) return (LWGEOM*)lwline_construct_empty(SRID, want3d, 0); cs = GEOSGeom_getCoordSeq(geom); pa = ptarray_from_GEOSCoordSeq(cs, want3d); return (LWGEOM*)lwline_construct(SRID, NULL, pa); case GEOS_POLYGON: LWDEBUG(4, "lwgeom_from_geometry: it's a Polygon"); if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoly_construct_empty(SRID, want3d, 0); ngeoms = GEOSGetNumInteriorRings(geom); ppaa = lwalloc(sizeof(POINTARRAY*) * (ngeoms + 1)); g = GEOSGetExteriorRing(geom); cs = GEOSGeom_getCoordSeq(g); ppaa[0] = ptarray_from_GEOSCoordSeq(cs, want3d); for (i = 0; i < ngeoms; i++) { g = GEOSGetInteriorRingN(geom, i); cs = GEOSGeom_getCoordSeq(g); ppaa[i + 1] = ptarray_from_GEOSCoordSeq(cs, want3d); } return (LWGEOM*)lwpoly_construct(SRID, NULL, ngeoms + 1, ppaa); case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_GEOMETRYCOLLECTION: LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi"); ngeoms = GEOSGetNumGeometries(geom); geoms = NULL; if (ngeoms) { geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); for (i = 0; i < ngeoms; i++) { g = GEOSGetGeometryN(geom, i); geoms[i] = GEOS2LWGEOM(g, want3d); } } return (LWGEOM*)lwcollection_construct(type, SRID, NULL, ngeoms, geoms); default: lwerror("GEOS2LWGEOM: unknown geometry type: %d", type); return NULL; } } GEOSCoordSeq ptarray_to_GEOSCoordSeq(const POINTARRAY*, uint8_t fix_ring); GEOSCoordSeq ptarray_to_GEOSCoordSeq(const POINTARRAY* pa, uint8_t fix_ring) { uint32_t dims = 2; uint32_t i; int append_points = 0; const POINT3D *p3d = NULL; const POINT2D* p2d = NULL; GEOSCoordSeq sq; if (FLAGS_GET_Z(pa->flags)) dims = 3; if (fix_ring) { if (pa->npoints < 1) { lwerror("ptarray_to_GEOSCoordSeq called with fix_ring and 0 vertices in ring, cannot fix"); return NULL; } else { if (pa->npoints < 4) append_points = 4 - pa->npoints; if (!ptarray_is_closed_2d(pa) && append_points == 0) append_points = 1; } } if (!(sq = GEOSCoordSeq_create(pa->npoints + append_points, dims))) { lwerror("Error creating GEOS Coordinate Sequence"); return NULL; } for (i = 0; i < pa->npoints; i++) { if (dims == 3) { p3d = getPoint3d_cp(pa, i); p2d = (const POINT2D*)p3d; LWDEBUGF(4, "Point: %g,%g,%g", p3d->x, p3d->y, p3d->z); } else { p2d = getPoint2d_cp(pa, i); LWDEBUGF(4, "Point: %g,%g", p2d->x, p2d->y); } #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(sq, i, p2d->x); GEOSCoordSeq_setY(sq, i, p2d->y); if (dims == 3) GEOSCoordSeq_setZ(sq, i, p3d->z); #else if (dims == 3) GEOSCoordSeq_setXYZ(sq, i, p2d->x, p2d->y, p3d->z); else GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y); #endif } if (append_points) { if (dims == 3) { p3d = getPoint3d_cp(pa, 0); p2d = (const POINT2D*)p3d; } else p2d = getPoint2d_cp(pa, 0); for (i = pa->npoints; i < pa->npoints + append_points; i++) { #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(sq, i, p2d->x); GEOSCoordSeq_setY(sq, i, p2d->y); #else GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y); #endif if (dims == 3) GEOSCoordSeq_setZ(sq, i, p3d->z); } } return sq; } static inline GEOSGeometry* ptarray_to_GEOSLinearRing(const POINTARRAY* pa, uint8_t autofix) { GEOSCoordSeq sq; GEOSGeom g; sq = ptarray_to_GEOSCoordSeq(pa, autofix); g = GEOSGeom_createLinearRing(sq); return g; } GEOSGeometry* GBOX2GEOS(const GBOX* box) { GEOSGeometry* envelope; GEOSGeometry* ring; GEOSCoordSequence* seq = GEOSCoordSeq_create(5, 2); if (!seq) return NULL; #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(seq, 0, box->xmin); GEOSCoordSeq_setY(seq, 0, box->ymin); GEOSCoordSeq_setX(seq, 1, box->xmax); GEOSCoordSeq_setY(seq, 1, box->ymin); GEOSCoordSeq_setX(seq, 2, box->xmax); GEOSCoordSeq_setY(seq, 2, box->ymax); GEOSCoordSeq_setX(seq, 3, box->xmin); GEOSCoordSeq_setY(seq, 3, box->ymax); GEOSCoordSeq_setX(seq, 4, box->xmin); GEOSCoordSeq_setY(seq, 4, box->ymin); #else GEOSCoordSeq_setXY(seq, 0, box->xmin, box->ymin); GEOSCoordSeq_setXY(seq, 1, box->xmax, box->ymin); GEOSCoordSeq_setXY(seq, 2, box->xmax, box->ymax); GEOSCoordSeq_setXY(seq, 3, box->xmin, box->ymax); GEOSCoordSeq_setXY(seq, 4, box->xmin, box->ymin); #endif ring = GEOSGeom_createLinearRing(seq); if (!ring) { GEOSCoordSeq_destroy(seq); return NULL; } envelope = GEOSGeom_createPolygon(ring, NULL, 0); if (!envelope) { GEOSGeom_destroy(ring); return NULL; } return envelope; } GEOSGeometry* LWGEOM2GEOS(const LWGEOM* lwgeom, uint8_t autofix) { GEOSCoordSeq sq; GEOSGeom g, shell; GEOSGeom* geoms = NULL; uint32_t ngeoms, i, j; int geostype; #if LWDEBUG_LEVEL >= 4 char* wkt; #endif if (autofix) { /* cross fingers and try without autofix, maybe it'll work? */ g = LWGEOM2GEOS(lwgeom, LW_FALSE); if (g) return g; } LWDEBUGF(4, "LWGEOM2GEOS got a %s", lwtype_name(lwgeom->type)); if (lwgeom_has_arc(lwgeom)) { LWGEOM* lwgeom_stroked = lwgeom_stroke(lwgeom, 32); GEOSGeometry* g = LWGEOM2GEOS(lwgeom_stroked, autofix); lwgeom_free(lwgeom_stroked); return g; } LWPOINT* lwp = NULL; LWPOLY* lwpoly = NULL; LWLINE* lwl = NULL; LWCOLLECTION* lwc = NULL; switch (lwgeom->type) { case POINTTYPE: lwp = (LWPOINT*)lwgeom; if (lwgeom_is_empty(lwgeom)) g = GEOSGeom_createEmptyPolygon(); else { #if POSTGIS_GEOS_VERSION < 38 sq = ptarray_to_GEOSCoordSeq(lwp->point, 0); g = GEOSGeom_createPoint(sq); #else if (lwgeom_has_z(lwgeom)) { sq = ptarray_to_GEOSCoordSeq(lwp->point, 0); g = GEOSGeom_createPoint(sq); } else { const POINT2D* p = getPoint2d_cp(lwp->point, 0); g = GEOSGeom_createPointFromXY(p->x, p->y); } #endif } if (!g) return NULL; break; case LINETYPE: lwl = (LWLINE*)lwgeom; /* TODO: if (autofix) */ if (lwl->points->npoints == 1) { /* Duplicate point, to make geos-friendly */ lwl->points = ptarray_addPoint(lwl->points, getPoint_internal(lwl->points, 0), FLAGS_NDIMS(lwl->points->flags), lwl->points->npoints); } sq = ptarray_to_GEOSCoordSeq(lwl->points, 0); g = GEOSGeom_createLineString(sq); if (!g) return NULL; break; case POLYGONTYPE: lwpoly = (LWPOLY*)lwgeom; if (lwgeom_is_empty(lwgeom)) g = GEOSGeom_createEmptyPolygon(); else { shell = ptarray_to_GEOSLinearRing(lwpoly->rings[0], autofix); if (!shell) return NULL; ngeoms = lwpoly->nrings - 1; if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms); for (i = 1; i < lwpoly->nrings; i++) { geoms[i - 1] = ptarray_to_GEOSLinearRing(lwpoly->rings[i], autofix); if (!geoms[i - 1]) { uint32_t k; for (k = 0; k < i - 1; k++) GEOSGeom_destroy(geoms[k]); lwfree(geoms); GEOSGeom_destroy(shell); return NULL; } } g = GEOSGeom_createPolygon(shell, geoms, ngeoms); if (geoms) lwfree(geoms); } if (!g) return NULL; break; case TRIANGLETYPE: if (lwgeom_is_empty(lwgeom)) g = GEOSGeom_createEmptyPolygon(); else { LWTRIANGLE *lwt = (LWTRIANGLE *)lwgeom; shell = ptarray_to_GEOSLinearRing(lwt->points, autofix); if (!shell) return NULL; g = GEOSGeom_createPolygon(shell, NULL, 0); } if (!g) return NULL; break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case TINTYPE: case COLLECTIONTYPE: if (lwgeom->type == MULTIPOINTTYPE) geostype = GEOS_MULTIPOINT; else if (lwgeom->type == MULTILINETYPE) geostype = GEOS_MULTILINESTRING; else if (lwgeom->type == MULTIPOLYGONTYPE) geostype = GEOS_MULTIPOLYGON; else geostype = GEOS_GEOMETRYCOLLECTION; lwc = (LWCOLLECTION*)lwgeom; ngeoms = lwc->ngeoms; if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms); j = 0; for (i = 0; i < ngeoms; ++i) { GEOSGeometry* g; if (lwgeom_is_empty(lwc->geoms[i])) continue; g = LWGEOM2GEOS(lwc->geoms[i], 0); if (!g) { uint32_t k; for (k = 0; k < j; k++) GEOSGeom_destroy(geoms[k]); lwfree(geoms); return NULL; } geoms[j++] = g; } g = GEOSGeom_createCollection(geostype, geoms, j); if (ngeoms > 0) lwfree(geoms); if (!g) return NULL; break; default: lwerror("Unknown geometry type: %d - %s", lwgeom->type, lwtype_name(lwgeom->type)); return NULL; } GEOSSetSRID(g, lwgeom->srid); #if LWDEBUG_LEVEL >= 4 wkt = GEOSGeomToWKT(g); LWDEBUGF(4, "LWGEOM2GEOS: GEOSGeom: %s", wkt); free(wkt); #endif return g; } GEOSGeometry* make_geos_point(double x, double y) { GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2); GEOSGeometry* geom = NULL; if (!seq) return NULL; #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(seq, 0, x); GEOSCoordSeq_setY(seq, 0, y); #else GEOSCoordSeq_setXY(seq, 0, x, y); #endif geom = GEOSGeom_createPoint(seq); if (!geom) GEOSCoordSeq_destroy(seq); return geom; } GEOSGeometry* make_geos_segment(double x1, double y1, double x2, double y2) { GEOSCoordSequence* seq = GEOSCoordSeq_create(2, 2); GEOSGeometry* geom = NULL; if (!seq) return NULL; #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(seq, 0, x1); GEOSCoordSeq_setY(seq, 0, y1); GEOSCoordSeq_setX(seq, 1, x2); GEOSCoordSeq_setY(seq, 1, y2); #else GEOSCoordSeq_setXY(seq, 0, x1, y1); GEOSCoordSeq_setXY(seq, 1, x2, y2); #endif geom = GEOSGeom_createLineString(seq); if (!geom) GEOSCoordSeq_destroy(seq); return geom; } const char* lwgeom_geos_version(void) { const char* ver = GEOSversion(); return ver; } /* Return the consistent SRID of all input. * Intended to be called from RESULT_SRID macro */ static int32_t get_result_srid(size_t count, const char* funcname, ...) { va_list ap; va_start(ap, funcname); int32_t srid = SRID_INVALID; size_t i; for(i = 0; i < count; i++) { LWGEOM* g = va_arg(ap, LWGEOM*); if (!g) { lwerror("%s: Geometry is null", funcname); return SRID_INVALID; } if (i == 0) { srid = g->srid; } else { if (g->srid != srid) { lwerror("%s: Operation on mixed SRID geometries (%d != %d)", funcname, srid, g->srid); return SRID_INVALID; } } } return srid; } LWGEOM* lwgeom_normalize(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry* g; if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); if (GEOSNormalize(g) == -1) GEOS_FREE_AND_FAIL(g); GEOSSetSRID(g, srid); if (!(result = GEOS2LWGEOM(g, is3d))) GEOS_FREE_AND_FAIL(g); GEOSGeom_destroy(g); return result; } LWGEOM* lwgeom_intersection(const LWGEOM* geom1, const LWGEOM* geom2) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry* g1; GEOSGeometry* g2; GEOSGeometry* g3; if (srid == SRID_INVALID) return NULL; /* A.Intersection(Empty) == Empty */ if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom2); /* match empty type? */ /* Empty.Intersection(A) == Empty */ if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */ initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSIntersection(g1, g2); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } LWGEOM* lwgeom_linemerge(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry* g1; GEOSGeometry* g3; if (srid == SRID_INVALID) return NULL; /* Empty.Linemerge() == Empty */ if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); /* match empty type to linestring? */ initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); g3 = GEOSLineMerge(g1); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } LWGEOM* lwgeom_unaryunion(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry* g1; GEOSGeometry* g3; if (srid == SRID_INVALID) return NULL; /* Empty.UnaryUnion() == Empty */ if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); g3 = GEOSUnaryUnion(g1); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } LWGEOM* lwgeom_difference(const LWGEOM* geom1, const LWGEOM* geom2) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry *g1, *g2, *g3; if (srid == SRID_INVALID) return NULL; /* A.Intersection(Empty) == Empty */ if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); /* match empty type? */ /* Empty.Intersection(A) == Empty */ if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */ initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSDifference(g1, g2); if (!g3) GEOS_FREE_AND_FAIL(g1, g2); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } LWGEOM* lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry *g1, *g2, *g3; if (srid == SRID_INVALID) return NULL; /* A.SymDifference(Empty) == A */ if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); /* Empty.DymDifference(B) == B */ if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2); initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSSymDifference(g1, g2); if (!g3) GEOS_FREE_AND_FAIL(g1, g2); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } LWGEOM* lwgeom_centroid(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry *g1, *g3; if (srid == SRID_INVALID) return NULL; if (lwgeom_is_empty(geom)) { LWPOINT* lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom)); return lwpoint_as_lwgeom(lwp); } initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); g3 = GEOSGetCentroid(g1); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1); GEOS_FREE(g1, g3); return result; } LWGEOM * lwgeom_pointonsurface(const LWGEOM *geom) { LWGEOM *result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry *g1, *g3; if (srid == SRID_INVALID) return NULL; if (lwgeom_is_empty(geom)) { LWPOINT *lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom)); return lwpoint_as_lwgeom(lwp); } initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); g3 = GEOSPointOnSurface(g1); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } LWGEOM* lwgeom_union(const LWGEOM* geom1, const LWGEOM* geom2) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry *g1, *g2, *g3; if (srid == SRID_INVALID) return NULL; /* A.Union(empty) == A */ if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2); /* B.Union(empty) == B */ if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSUnion(g1, g2); if (!g3) GEOS_FREE_AND_FAIL(g1, g2); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x1, double y1, double x2, double y2) { LWGEOM *result; GEOSGeometry *g1, *g3; int is3d; /* A.Intersection(Empty) == Empty */ if ( lwgeom_is_empty(geom1) ) return lwgeom_clone_deep(geom1); is3d = FLAGS_GET_Z(geom1->flags); initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL_DEBUG(); if (!(g3 = GEOSClipByRect(g1, x1, y1, x2, y2))) GEOS_FREE_AND_FAIL_DEBUG(g1); GEOS_FREE(g1); result = GEOS2LWGEOM(g3, is3d); GEOS_FREE(g3); if (!result) GEOS_FAIL_DEBUG(); result->srid = geom1->srid; return result; } /* ------------ BuildArea stuff ---------------------------------------------------------------------{ */ #if POSTGIS_GEOS_VERSION < 38 typedef struct Face_t { const GEOSGeometry* geom; GEOSGeometry* env; double envarea; struct Face_t* parent; /* if this face is an hole of another one, or NULL */ } Face; static Face* newFace(const GEOSGeometry* g); static void delFace(Face* f); static unsigned int countParens(const Face* f); static void findFaceHoles(Face** faces, int nfaces); static Face* newFace(const GEOSGeometry* g) { Face* f = lwalloc(sizeof(Face)); f->geom = g; f->env = GEOSEnvelope(f->geom); GEOSArea(f->env, &f->envarea); f->parent = NULL; return f; } static unsigned int countParens(const Face* f) { unsigned int pcount = 0; while (f->parent) { ++pcount; f = f->parent; } return pcount; } /* Destroy the face and release memory associated with it */ static void delFace(Face* f) { GEOSGeom_destroy(f->env); lwfree(f); } static int compare_by_envarea(const void* g1, const void* g2) { Face* f1 = *(Face**)g1; Face* f2 = *(Face**)g2; double n1 = f1->envarea; double n2 = f2->envarea; if (n1 < n2) return 1; if (n1 > n2) return -1; return 0; } /* Find holes of each face */ static void findFaceHoles(Face** faces, int nfaces) { int i, j, h; /* We sort by envelope area so that we know holes are only after their shells */ qsort(faces, nfaces, sizeof(Face*), compare_by_envarea); for (i = 0; i < nfaces; ++i) { Face* f = faces[i]; int nholes = GEOSGetNumInteriorRings(f->geom); LWDEBUGF(2, "Scanning face %d with env area %g and %d holes", i, f->envarea, nholes); for (h = 0; h < nholes; ++h) { const GEOSGeometry* hole = GEOSGetInteriorRingN(f->geom, h); LWDEBUGF(2, "Looking for hole %d/%d of face %d among %d other faces", h + 1, nholes, i, nfaces - i - 1); for (j = i + 1; j < nfaces; ++j) { const GEOSGeometry* f2er; Face* f2 = faces[j]; if (f2->parent) continue; /* hole already assigned */ f2er = GEOSGetExteriorRing(f2->geom); /* TODO: can be optimized as the ring would have the same vertices, possibly in * different order. Maybe comparing number of points could already be useful. */ if (GEOSEquals(f2er, hole)) { LWDEBUGF(2, "Hole %d/%d of face %d is face %d", h + 1, nholes, i, j); f2->parent = f; break; } } } } } static GEOSGeometry* collectFacesWithEvenAncestors(Face** faces, int nfaces) { GEOSGeometry** geoms = lwalloc(sizeof(GEOSGeometry*) * nfaces); GEOSGeometry* ret; unsigned int ngeoms = 0; int i; for (i = 0; i < nfaces; ++i) { Face* f = faces[i]; if (countParens(f) % 2) continue; /* we skip odd parents geoms */ geoms[ngeoms++] = GEOSGeom_clone(f->geom); } ret = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, ngeoms); lwfree(geoms); return ret; } GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in) { GEOSGeometry* tmp; GEOSGeometry *geos_result, *shp; GEOSGeometry const* vgeoms[1]; uint32_t i, ngeoms; int srid = GEOSGetSRID(geom_in); Face** geoms; #if POSTGIS_DEBUG_LEVEL >= 3 LWGEOM *geos_geom; char *geom_ewkt; #endif vgeoms[0] = geom_in; geos_result = GEOSPolygonize(vgeoms, 1); LWDEBUGF(3, "GEOSpolygonize returned @ %p", geos_result); /* Null return from GEOSpolygonize (an exception) */ if (!geos_result) return 0; /* We should now have a collection */ #if PARANOIA_LEVEL > 0 if (GEOSGeomTypeId(geos_result) != COLLECTIONTYPE) { GEOSGeom_destroy(geos_result); lwerror("%s [%d] Unexpected return from GEOSpolygonize", __FILE__, __LINE__); return 0; } #endif ngeoms = GEOSGetNumGeometries(geos_result); #if POSTGIS_DEBUG_LEVEL >= 3 LWDEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms); geos_geom = GEOS2LWGEOM(geos_result, 0); geom_ewkt = lwgeom_to_ewkt(geos_geom); LWDEBUGF(3, "GEOSpolygonize: polygonized:%s", geom_ewkt); lwgeom_free(geos_geom); lwfree(geom_ewkt); #endif /* No geometries in collection, early out */ if (ngeoms == 0) { GEOSSetSRID(geos_result, srid); return geos_result; } /* Return first geometry if we only have one in collection, to avoid the unnecessary Geometry clone below. */ if (ngeoms == 1) { tmp = (GEOSGeometry*)GEOSGetGeometryN(geos_result, 0); if (!tmp) { GEOSGeom_destroy(geos_result); return 0; /* exception */ } shp = GEOSGeom_clone(tmp); GEOSGeom_destroy(geos_result); /* only safe after the clone above */ GEOSSetSRID(shp, srid); return shp; } LWDEBUGF(2, "Polygonize returned %d geoms", ngeoms); /* * Polygonizer returns a polygon for each face in the built topology. * * This means that for any face with holes we'll have other faces representing each hole. We can imagine a * parent-child relationship between these faces. * * In order to maximize the number of visible rings in output we only use those faces which have an even number * of parents. * * Example: * * +---------------+ * | L0 | L0 has no parents * | +---------+ | * | | L1 | | L1 is an hole of L0 * | | +---+ | | * | | |L2 | | | L2 is an hole of L1 (which is an hole of L0) * | | | | | | * | | +---+ | | * | +---------+ | * | | * +---------------+ * * See http://trac.osgeo.org/postgis/ticket/1806 * */ /* Prepare face structures for later analysis */ geoms = lwalloc(sizeof(Face**) * ngeoms); for (i = 0; i < ngeoms; ++i) geoms[i] = newFace(GEOSGetGeometryN(geos_result, i)); /* Find faces representing other faces holes */ findFaceHoles(geoms, ngeoms); /* Build a MultiPolygon composed only by faces with an even number of ancestors */ tmp = collectFacesWithEvenAncestors(geoms, ngeoms); /* Cleanup face structures */ for (i = 0; i < ngeoms; ++i) delFace(geoms[i]); lwfree(geoms); /* Faces referenced memory owned by geos_result. It is safe to destroy geos_result after deleting them. */ GEOSGeom_destroy(geos_result); /* Run a single overlay operation to dissolve shared edges */ shp = GEOSUnionCascaded(tmp); if (!shp) { GEOSGeom_destroy(tmp); return 0; /* exception */ } GEOSGeom_destroy(tmp); GEOSSetSRID(shp, srid); return shp; } #endif LWGEOM* lwgeom_buildarea(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry *g1, *g3; if (srid == SRID_INVALID) return NULL; /* Can't build an area from an empty! */ if (lwgeom_is_empty(geom)) return (LWGEOM*)lwpoly_construct_empty(srid, is3d, 0); initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); #if POSTGIS_GEOS_VERSION < 38 g3 = LWGEOM_GEOS_buildArea(g1); #else g3 = GEOSBuildArea(g1); #endif if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); /* If no geometries are in result collection, return NULL */ if (GEOSGetNumGeometries(g3) == 0) { GEOS_FREE(g1); return NULL; } if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } /* ------------ end of BuildArea stuff ---------------------------------------------------------------------} */ int lwgeom_is_simple(const LWGEOM* geom) { GEOSGeometry* g; int simple; /* Empty is always simple */ if (lwgeom_is_empty(geom)) return LW_TRUE; initGEOS(lwnotice, lwgeom_geos_error); if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) return -1; simple = GEOSisSimple(g); GEOSGeom_destroy(g); if (simple == 2) /* exception thrown */ { lwerror("lwgeom_is_simple: %s", lwgeom_geos_errmsg); return -1; } return simple ? LW_TRUE : LW_FALSE; } LWGEOM* lwgeom_geos_noop(const LWGEOM* geom) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry* g; if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); if (!g) GEOS_FREE_AND_FAIL(g); GEOSSetSRID(g, srid); if (!(result = GEOS2LWGEOM(g, is3d))) GEOS_FREE_AND_FAIL(g); GEOS_FREE(g); return result; } LWGEOM* lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry *g1, *g2, *g3; if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSSnap(g1, g2, tolerance); if (!g3) GEOS_FREE_AND_FAIL(g1, g2); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } LWGEOM* lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2) { LWGEOM* result; int32_t srid = RESULT_SRID(geom1, geom2); uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); GEOSGeometry *g1, *g2, *g3; if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); g3 = GEOSSharedPaths(g1, g2); if (!g3) GEOS_FREE_AND_FAIL(g1, g2); GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); GEOS_FREE(g1, g2, g3); return result; } static LWGEOM * lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit) { LWGEOM* result; LWGEOM* geom = lwline_as_lwgeom(lwline); int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry *g1, *g3; if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit); if (!g3) { GEOS_FREE(g1); return NULL; } GEOSSetSRID(g3, srid); if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } static LWGEOM * lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit) { const LWGEOM *geom = lwcollection_as_lwgeom(col); int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(col->flags); LWCOLLECTION *result; LWGEOM *tmp; uint32_t i; if (srid == SRID_INVALID) return NULL; result = lwcollection_construct_empty(MULTILINETYPE, srid, is3d, LW_FALSE); for (i = 0; i < col->ngeoms; i++) { tmp = lwgeom_offsetcurve(col->geoms[i], size, quadsegs, joinStyle, mitreLimit); if (!tmp) { lwcollection_free(result); return NULL; } if (!lwgeom_is_empty(tmp)) { if (lwgeom_is_collection(tmp)) result = lwcollection_concat_in_place(result, lwgeom_as_lwcollection(tmp)); else result = lwcollection_add_lwgeom(result, tmp); if (!result) { lwgeom_free(tmp); return NULL; } } } if (result->ngeoms == 1) { tmp = result->geoms[0]; lwcollection_release(result); return tmp; } else return lwcollection_as_lwgeom(result); } LWGEOM* lwgeom_offsetcurve(const LWGEOM* geom, double size, int quadsegs, int joinStyle, double mitreLimit) { int32_t srid = RESULT_SRID(geom); LWGEOM *result = NULL; LWGEOM *noded = NULL; if (srid == SRID_INVALID) return NULL; if (lwgeom_dimension(geom) != 1) { lwerror("%s: input is not linear", __func__, lwtype_name(geom->type)); return NULL; } while (!result) { switch (geom->type) { case LINETYPE: result = lwline_offsetcurve(lwgeom_as_lwline(geom), size, quadsegs, joinStyle, mitreLimit); break; case COLLECTIONTYPE: case MULTILINETYPE: result = lwcollection_offsetcurve(lwgeom_as_lwcollection(geom), size, quadsegs, joinStyle, mitreLimit); break; default: lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type)); return NULL; } if (result) { if (noded) lwgeom_free(noded); return result; } else if (!noded) { noded = lwgeom_node(geom); if (!noded) { lwerror("lwgeom_offsetcurve: cannot node input"); return NULL; } geom = noded; } else { lwgeom_free(noded); lwerror("lwgeom_offsetcurve: noded geometry cannot be offset"); return NULL; } } return result; } LWMPOINT* lwpoly_to_points(const LWPOLY* lwpoly, uint32_t npoints, int32_t seed) { double area, bbox_area, bbox_width, bbox_height; GBOX bbox; const LWGEOM* lwgeom = (LWGEOM*)lwpoly; uint32_t sample_npoints, sample_sqrt, sample_width, sample_height; double sample_cell_size; uint32_t i, j, n; uint32_t iterations = 0; uint32_t npoints_generated = 0; uint32_t npoints_tested = 0; GEOSGeometry* g; const GEOSPreparedGeometry* gprep; GEOSGeometry* gpt; GEOSCoordSequence* gseq; LWMPOINT* mpt; int32_t srid = lwgeom_get_srid(lwgeom); int done = 0; int* cells; const size_t size = 2 * sizeof(int); char tmp[2 * sizeof(int)]; const size_t stride = 2 * sizeof(int); if (lwgeom_get_type(lwgeom) != POLYGONTYPE) { lwerror("%s: only polygons supported", __func__); return NULL; } if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL; if (!lwpoly->bbox) lwgeom_calculate_gbox(lwgeom, &bbox); else bbox = *(lwpoly->bbox); area = lwpoly_area(lwpoly); bbox_width = bbox.xmax - bbox.xmin; bbox_height = bbox.ymax - bbox.ymin; bbox_area = bbox_width * bbox_height; if (area == 0.0 || bbox_area == 0.0) { lwerror("%s: zero area input polygon, TBD", __func__); return NULL; } /* Gross up our test set a bit to increase odds of getting coverage in one pass */ sample_npoints = npoints * bbox_area / area; /* We're going to generate points using a sample grid as described * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform * "random" set of points. So we have to figure out how to stick a grid into our box */ sample_sqrt = lround(sqrt(sample_npoints)); if (sample_sqrt == 0) sample_sqrt = 1; /* Calculate the grids we're going to randomize within */ if (bbox_width > bbox_height) { sample_width = sample_sqrt; sample_height = ceil((double)sample_npoints / (double)sample_width); sample_cell_size = bbox_width / sample_width; } else { sample_height = sample_sqrt; sample_width = ceil((double)sample_npoints / (double)sample_height); sample_cell_size = bbox_height / sample_height; } /* Prepare the polygon for fast true/false testing */ initGEOS(lwnotice, lwgeom_geos_error); g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0); if (!g) { lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg); return NULL; } gprep = GEOSPrepare(g); /* Get an empty multi-point ready to return */ mpt = lwmpoint_construct_empty(srid, 0, 0); /* Initiate random number generator. * Repeatable numbers are generated with seed values >= 1. * When seed is zero and has not previously been set, it is based on * Unix time (seconds) and process ID. */ lwrandom_set_seed(seed); /* Now we fill in an array of cells, and then shuffle that array, */ /* so we can visit the cells in random order to avoid visual ugliness */ /* caused by visiting them sequentially */ cells = lwalloc(2 * sizeof(int) * sample_height * sample_width); for (i = 0; i < sample_width; i++) { for (j = 0; j < sample_height; j++) { cells[2 * (i * sample_height + j)] = i; cells[2 * (i * sample_height + j) + 1] = j; } } /* Fisher-Yates shuffle */ n = sample_height * sample_width; if (n > 1) { for (i = n - 1; i > 0; i--) { size_t j = (size_t)(lwrandom_uniform() * (i + 1)); memcpy(tmp, (char *)cells + j * stride, size); memcpy((char *)cells + j * stride, (char *)cells + i * stride, size); memcpy((char *)cells + i * stride, tmp, size); } } /* Start testing points */ while (npoints_generated < npoints) { iterations++; for (i = 0; i < sample_width * sample_height; i++) { int contains = 0; double y = bbox.ymin + cells[2 * i] * sample_cell_size; double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size; x += lwrandom_uniform() * sample_cell_size; y += lwrandom_uniform() * sample_cell_size; if (x >= bbox.xmax || y >= bbox.ymax) continue; gseq = GEOSCoordSeq_create(1, 2); #if POSTGIS_GEOS_VERSION < 38 GEOSCoordSeq_setX(gseq, 0, x); GEOSCoordSeq_setY(gseq, 0, y); #else GEOSCoordSeq_setXY(gseq, 0, x, y); #endif gpt = GEOSGeom_createPoint(gseq); contains = GEOSPreparedIntersects(gprep, gpt); GEOSGeom_destroy(gpt); if (contains == 2) { GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg); return NULL; } if (contains) { npoints_generated++; mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y)); if (npoints_generated == npoints) { done = 1; break; } } /* Short-circuit check for ctrl-c occasionally */ npoints_tested++; if (npoints_tested % 10000 == 0) LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL); if (done) break; } if (done || iterations > 100) break; } GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); lwfree(cells); return mpt; } /* Allocate points to sub-geometries by area, then call lwgeom_poly_to_points and bundle up final result in a single * multipoint. */ LWMPOINT* lwmpoly_to_points(const LWMPOLY* lwmpoly, uint32_t npoints, int32_t seed) { const LWGEOM* lwgeom = (LWGEOM*)lwmpoly; double area; uint32_t i; LWMPOINT* mpt = NULL; if (lwgeom_get_type(lwgeom) != MULTIPOLYGONTYPE) { lwerror("%s: only multipolygons supported", __func__); return NULL; } if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL; area = lwgeom_area(lwgeom); for (i = 0; i < lwmpoly->ngeoms; i++) { double sub_area = lwpoly_area(lwmpoly->geoms[i]); int sub_npoints = lround(npoints * sub_area / area); if (sub_npoints > 0) { LWMPOINT* sub_mpt = lwpoly_to_points(lwmpoly->geoms[i], sub_npoints, seed); if (!mpt) mpt = sub_mpt; else { uint32_t j; for (j = 0; j < sub_mpt->ngeoms; j++) mpt = lwmpoint_add_lwpoint(mpt, sub_mpt->geoms[j]); /* Just free the shell, leave the underlying lwpoints alone, as they are now owned by * the returning multipoint */ lwfree(sub_mpt->geoms); lwgeom_release((LWGEOM*)sub_mpt); } } } return mpt; } LWMPOINT* lwgeom_to_points(const LWGEOM* lwgeom, uint32_t npoints, int32_t seed) { switch (lwgeom_get_type(lwgeom)) { case MULTIPOLYGONTYPE: return lwmpoly_to_points((LWMPOLY*)lwgeom, npoints, seed); case POLYGONTYPE: return lwpoly_to_points((LWPOLY*)lwgeom, npoints, seed); default: lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(lwgeom_get_type(lwgeom))); return NULL; } } LWTIN* lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d) { int type = GEOSGeomTypeId(geom); int SRID = GEOSGetSRID(geom); /* GEOS's 0 is equivalent to our unknown as for SRID values */ if (SRID == 0) SRID = SRID_UNKNOWN; if (want3d && !GEOSHasZ(geom)) { LWDEBUG(3, "Geometry has no Z, won't provide one"); want3d = 0; } switch (type) { LWTRIANGLE** geoms; uint32_t i, ngeoms; case GEOS_GEOMETRYCOLLECTION: LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi"); ngeoms = GEOSGetNumGeometries(geom); geoms = NULL; if (ngeoms) { geoms = lwalloc(ngeoms * sizeof *geoms); if (!geoms) { lwerror("lwtin_from_geos: can't allocate geoms"); return NULL; } for (i = 0; i < ngeoms; i++) { const GEOSGeometry *poly, *ring; const GEOSCoordSequence* cs; POINTARRAY* pa; poly = GEOSGetGeometryN(geom, i); ring = GEOSGetExteriorRing(poly); cs = GEOSGeom_getCoordSeq(ring); pa = ptarray_from_GEOSCoordSeq(cs, want3d); geoms[i] = lwtriangle_construct(SRID, NULL, pa); } } return (LWTIN*)lwcollection_construct(TINTYPE, SRID, NULL, ngeoms, (LWGEOM**)geoms); case GEOS_POLYGON: case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_LINESTRING: case GEOS_LINEARRING: case GEOS_POINT: lwerror("lwtin_from_geos: invalid geometry type for tin: %d", type); break; default: lwerror("GEOS2LWGEOM: unknown geometry type: %d", type); return NULL; } /* shouldn't get here */ return NULL; } /* * output = 1 for edges, 2 for TIN, 0 for polygons */ LWGEOM* lwgeom_delaunay_triangulation(const LWGEOM* geom, double tolerance, int32_t output) { LWGEOM* result; int32_t srid = RESULT_SRID(geom); uint8_t is3d = FLAGS_GET_Z(geom->flags); GEOSGeometry *g1, *g3; if (output < 0 || output > 2) { lwerror("%s: invalid output type specified %d", __func__, output); return NULL; } if (srid == SRID_INVALID) return NULL; initGEOS(lwnotice, lwgeom_geos_error); if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); /* if output != 1 we want polys */ g3 = GEOSDelaunayTriangulation(g1, tolerance, output == 1); if (!g3) GEOS_FREE_AND_FAIL(g1); GEOSSetSRID(g3, srid); if (output == 2) { result = (LWGEOM*)lwtin_from_geos(g3, is3d); if (!result) { GEOS_FREE(g1, g3); lwerror("%s: cannot convert output geometry", __func__); return NULL; } lwgeom_set_srid(result, srid); } else if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g3); GEOS_FREE(g1, g3); return result; } static GEOSCoordSequence* lwgeom_get_geos_coordseq_2d(const LWGEOM* g, uint32_t num_points) { uint32_t i = 0; uint8_t num_dims = 2; LWPOINTITERATOR* it; GEOSCoordSequence* coords; POINT4D tmp; coords = GEOSCoordSeq_create(num_points, num_dims); if (!coords) return NULL; it = lwpointiterator_create(g); while (lwpointiterator_next(it, &tmp)) { if (i >= num_points) { lwerror("Incorrect num_points provided to lwgeom_get_geos_coordseq_2d"); GEOSCoordSeq_destroy(coords); lwpointiterator_destroy(it); return NULL; } #if POSTGIS_GEOS_VERSION < 38 if (!GEOSCoordSeq_setX(coords, i, tmp.x) || !GEOSCoordSeq_setY(coords, i, tmp.y)) #else if (!GEOSCoordSeq_setXY(coords, i, tmp.x, tmp.y)) #endif { GEOSCoordSeq_destroy(coords); lwpointiterator_destroy(it); return NULL; } i++; } lwpointiterator_destroy(it); return coords; } LWGEOM* lwgeom_voronoi_diagram(const LWGEOM* g, const GBOX* env, double tolerance, int output_edges) { uint32_t num_points = lwgeom_count_vertices(g); LWGEOM* lwgeom_result; char is_3d = LW_FALSE; int32_t srid = lwgeom_get_srid(g); GEOSCoordSequence* coords; GEOSGeometry* geos_geom; GEOSGeometry* geos_env = NULL; GEOSGeometry* geos_result; if (num_points < 2) { LWCOLLECTION* empty = lwcollection_construct_empty(COLLECTIONTYPE, lwgeom_get_srid(g), 0, 0); return lwcollection_as_lwgeom(empty); } initGEOS(lwnotice, lwgeom_geos_error); /* Instead of using the standard LWGEOM2GEOS transformer, we read the vertices of the LWGEOM directly and put * them into a single GEOS CoordinateSeq that can be used to define a LineString. This allows us to process * geometry types that may not be supported by GEOS, and reduces the memory requirements in cases of many * geometries with few points (such as LWMPOINT).*/ coords = lwgeom_get_geos_coordseq_2d(g, num_points); if (!coords) return NULL; geos_geom = GEOSGeom_createLineString(coords); if (!geos_geom) { GEOSCoordSeq_destroy(coords); return NULL; } if (env) geos_env = GBOX2GEOS(env); geos_result = GEOSVoronoiDiagram(geos_geom, geos_env, tolerance, output_edges); GEOSGeom_destroy(geos_geom); if (env) GEOSGeom_destroy(geos_env); if (!geos_result) { lwerror("GEOSVoronoiDiagram: %s", lwgeom_geos_errmsg); return NULL; } lwgeom_result = GEOS2LWGEOM(geos_result, is_3d); GEOSGeom_destroy(geos_result); lwgeom_set_srid(lwgeom_result, srid); return lwgeom_result; } lwgeom/src/liblwgeom/liblwgeom_internal.h0000644000176200001440000003616213773172540020363 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2011-2012 Sandro Santilli * Copyright (C) 2011 Paul Ramsey * Copyright (C) 2007-2008 Mark Cave-Ayland * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef _LIBLWGEOM_INTERNAL_H #define _LIBLWGEOM_INTERNAL_H 1 #include "../postgis_config.h" #include "lwgeom_log.h" #include #include #include #include #include #include #include #include #if HAVE_IEEEFP_H #include #endif #include "liblwgeom.h" /** * Floating point comparators. */ #define FP_TOLERANCE 1e-12 #define FP_IS_ZERO(A) (fabs(A) <= FP_TOLERANCE) #define FP_MAX(A, B) (((A) > (B)) ? (A) : (B)) #define FP_MIN(A, B) (((A) < (B)) ? (A) : (B)) #define FP_ABS(a) ((a) < (0) ? -(a) : (a)) #define FP_EQUALS(A, B) (fabs((A)-(B)) <= FP_TOLERANCE) #define FP_NEQUALS(A, B) (fabs((A)-(B)) > FP_TOLERANCE) #define FP_LT(A, B) (((A) + FP_TOLERANCE) < (B)) #define FP_LTEQ(A, B) (((A) - FP_TOLERANCE) <= (B)) #define FP_GT(A, B) (((A) - FP_TOLERANCE) > (B)) #define FP_GTEQ(A, B) (((A) + FP_TOLERANCE) >= (B)) #define FP_CONTAINS_TOP(A, X, B) (FP_LT(A, X) && FP_LTEQ(X, B)) #define FP_CONTAINS_BOTTOM(A, X, B) (FP_LTEQ(A, X) && FP_LT(X, B)) #define FP_CONTAINS_INCL(A, X, B) (FP_LTEQ(A, X) && FP_LTEQ(X, B)) #define FP_CONTAINS_EXCL(A, X, B) (FP_LT(A, X) && FP_LT(X, B)) #define FP_CONTAINS(A, X, B) FP_CONTAINS_EXCL(A, X, B) /* * this will change to NaN when I figure out how to * get NaN in a platform-independent way */ #define NO_VALUE 0.0 #define NO_Z_VALUE NO_VALUE #define NO_M_VALUE NO_VALUE /** * Well-Known Text (WKT) Output Variant Types */ #define WKT_NO_TYPE 0x08 /* Internal use only */ #define WKT_NO_PARENS 0x10 /* Internal use only */ #define WKT_IS_CHILD 0x20 /* Internal use only */ /** * Well-Known Binary (WKB) Output Variant Types */ #define WKB_DOUBLE_SIZE 8 /* Internal use only */ #define WKB_INT_SIZE 4 /* Internal use only */ #define WKB_BYTE_SIZE 1 /* Internal use only */ /** * Well-Known Binary (WKB) Geometry Types */ #define WKB_POINT_TYPE 1 #define WKB_LINESTRING_TYPE 2 #define WKB_POLYGON_TYPE 3 #define WKB_MULTIPOINT_TYPE 4 #define WKB_MULTILINESTRING_TYPE 5 #define WKB_MULTIPOLYGON_TYPE 6 #define WKB_GEOMETRYCOLLECTION_TYPE 7 #define WKB_CIRCULARSTRING_TYPE 8 #define WKB_COMPOUNDCURVE_TYPE 9 #define WKB_CURVEPOLYGON_TYPE 10 #define WKB_MULTICURVE_TYPE 11 #define WKB_MULTISURFACE_TYPE 12 #define WKB_CURVE_TYPE 13 /* from ISO draft, not sure is real */ #define WKB_SURFACE_TYPE 14 /* from ISO draft, not sure is real */ #define WKB_POLYHEDRALSURFACE_TYPE 15 #define WKB_TIN_TYPE 16 #define WKB_TRIANGLE_TYPE 17 /** * Macro for reading the size from the GSERIALIZED size attribute. * Cribbed from PgSQL, top 30 bits are size. Use VARSIZE() when working * internally with PgSQL. See SET_VARSIZE_4B / VARSIZE_4B in * PGSRC/src/include/postgres.h for details. */ #ifdef WORDS_BIGENDIAN #define SIZE_GET(varsize) ((varsize) & 0x3FFFFFFF) #define SIZE_SET(varsize, len) ((varsize) = ((len) & 0x3FFFFFFF)) #define IS_BIG_ENDIAN 1 #else #define SIZE_GET(varsize) (((varsize) >> 2) & 0x3FFFFFFF) #define SIZE_SET(varsize, len) ((varsize) = (((uint32_t)(len)) << 2)) #define IS_BIG_ENDIAN 0 #endif /** * Macro that returns: * -1 if n < 0, * 1 if n > 0, * 0 if n == 0 */ #define SIGNUM(n) (((n) > 0) - ((n) < 0)) /** * Tolerance used to determine equality. */ #define EPSILON_SQLMM 1e-8 /* * Export functions */ #define OUT_MAX_DOUBLE 1E15 #define OUT_SHOW_DIGS_DOUBLE 20 #define OUT_MAX_DOUBLE_PRECISION 15 #define OUT_MAX_DIGS_DOUBLE (OUT_SHOW_DIGS_DOUBLE + 2) /* +2 mean add dot and sign */ #define OUT_DOUBLE_BUFFER_SIZE \ OUT_MAX_DIGS_DOUBLE + OUT_MAX_DOUBLE_PRECISION + 1 /** * Constants for point-in-polygon return values */ #define LW_INSIDE 1 #define LW_BOUNDARY 0 #define LW_OUTSIDE -1 /* * Internal prototypes */ /* Machine endianness */ #define XDR 0 /* big endian */ #define NDR 1 /* little endian */ /* * Force dims */ LWGEOM* lwgeom_force_dims(const LWGEOM *lwgeom, int hasz, int hasm); LWPOINT* lwpoint_force_dims(const LWPOINT *lwpoint, int hasz, int hasm); LWLINE* lwline_force_dims(const LWLINE *lwline, int hasz, int hasm); LWPOLY* lwpoly_force_dims(const LWPOLY *lwpoly, int hasz, int hasm); LWCOLLECTION* lwcollection_force_dims(const LWCOLLECTION *lwcol, int hasz, int hasm); POINTARRAY* ptarray_force_dims(const POINTARRAY *pa, int hasz, int hasm); /** * Swap ordinate values o1 and o2 on a given POINTARRAY * * Ordinates semantic is: 0=x 1=y 2=z 3=m */ void ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2); /* * Is Empty? */ int lwpoly_is_empty(const LWPOLY *poly); int lwcollection_is_empty(const LWCOLLECTION *col); int lwcircstring_is_empty(const LWCIRCSTRING *circ); int lwtriangle_is_empty(const LWTRIANGLE *triangle); int lwline_is_empty(const LWLINE *line); int lwpoint_is_empty(const LWPOINT *point); /* * Number of vertices? */ uint32_t lwline_count_vertices(LWLINE *line); uint32_t lwpoly_count_vertices(LWPOLY *poly); uint32_t lwcollection_count_vertices(LWCOLLECTION *col); /* * DP simplification */ /** * @param minpts minimum number of points to retain, if possible. */ void ptarray_simplify_in_place(POINTARRAY *pa, double tolerance, uint32_t minpts); /* * The possible ways a pair of segments can interact. Returned by lw_segment_intersects */ enum CG_SEGMENT_INTERSECTION_TYPE { SEG_ERROR = -1, SEG_NO_INTERSECTION = 0, SEG_COLINEAR = 1, SEG_CROSS_LEFT = 2, SEG_CROSS_RIGHT = 3, SEG_TOUCH_LEFT = 4, SEG_TOUCH_RIGHT = 5 }; /* * Do the segments intersect? How? */ int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2); /* * Get/Set an enumeratoed ordinate. (x,y,z,m) */ double lwpoint_get_ordinate(const POINT4D *p, char ordinate); void lwpoint_set_ordinate(POINT4D *p, char ordinate, double value); /* * Generate an interpolated coordinate p given an interpolation value and ordinate to apply it to */ int point_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int hasz, int hasm, char ordinate, double interpolation_value); /* * Geohash */ int lwgeom_geohash_precision(GBOX bbox, GBOX *bounds); char *geohash_point(double longitude, double latitude, int precision); void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision); /* * Point comparisons */ int p4d_same(const POINT4D *p1, const POINT4D *p2); int p3d_same(const POINT3D *p1, const POINT3D *p2); int p2d_same(const POINT2D *p1, const POINT2D *p2); /* * Area calculations */ double lwpoly_area(const LWPOLY *poly); double lwcurvepoly_area(const LWCURVEPOLY *curvepoly); double lwtriangle_area(const LWTRIANGLE *triangle); /** * Pull a #GBOX from the header of a #GSERIALIZED, if one is available. If * it is not, return LW_FAILURE. */ int gserialized_read_gbox_p(const GSERIALIZED *g, GBOX *gbox); /* * Populate a bounding box *without* allocating an LWGEOM. Useful for some performance * purposes. Use only if gserialized_read_gbox_p failed */ int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Calculate required memory segment to contain a serialized form of the LWGEOM. * Primarily used internally by the serialization code. Exposed to allow the cunit * tests to exercise it. */ size_t gserialized_from_lwgeom_size(const LWGEOM *geom); /* * Length calculations */ double lwcompound_length(const LWCOMPOUND *comp); double lwcompound_length_2d(const LWCOMPOUND *comp); double lwline_length(const LWLINE *line); double lwline_length_2d(const LWLINE *line); double lwcircstring_length(const LWCIRCSTRING *circ); double lwcircstring_length_2d(const LWCIRCSTRING *circ); double lwpoly_perimeter(const LWPOLY *poly); double lwpoly_perimeter_2d(const LWPOLY *poly); double lwcurvepoly_perimeter(const LWCURVEPOLY *poly); double lwcurvepoly_perimeter_2d(const LWCURVEPOLY *poly); double lwtriangle_perimeter(const LWTRIANGLE *triangle); double lwtriangle_perimeter_2d(const LWTRIANGLE *triangle); /* * Segmentization */ LWPOLY *lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad); /* * Affine */ void ptarray_affine(POINTARRAY *pa, const AFFINE *affine); void affine_invert(AFFINE *affine); /* * Scale */ void ptarray_scale(POINTARRAY *pa, const POINT4D *factor); /* * PointArray */ int ptarray_has_z(const POINTARRAY *pa); int ptarray_has_m(const POINTARRAY *pa); double ptarray_signed_area(const POINTARRAY *pa); /* * Length */ double ptarray_length(const POINTARRAY *pts); double ptarray_arc_length_2d(const POINTARRAY *pts); /* * Clone support */ LWPOINT *lwpoint_clone(const LWPOINT *lwgeom); LWLINE *lwline_clone(const LWLINE *lwgeom); LWPOLY *lwpoly_clone(const LWPOLY *lwgeom); LWTRIANGLE *lwtriangle_clone(const LWTRIANGLE *lwgeom); LWCOLLECTION *lwcollection_clone(const LWCOLLECTION *lwgeom); LWCIRCSTRING *lwcircstring_clone(const LWCIRCSTRING *curve); POINTARRAY *ptarray_clone(const POINTARRAY *ptarray); LWLINE *lwline_clone_deep(const LWLINE *lwgeom); LWPOLY *lwpoly_clone_deep(const LWPOLY *lwgeom); LWCOLLECTION *lwcollection_clone_deep(const LWCOLLECTION *lwgeom); GBOX *gbox_clone(const GBOX *gbox); /* * Clockwise */ void lwpoly_force_clockwise(LWPOLY *poly); void lwtriangle_force_clockwise(LWTRIANGLE *triangle); int lwpoly_is_clockwise(LWPOLY *poly); int lwtriangle_is_clockwise(LWTRIANGLE *triangle); int ptarray_isccw(const POINTARRAY *pa); /* * Same */ char ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2); char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2); char lwline_same(const LWLINE *p1, const LWLINE *p2); char lwpoly_same(const LWPOLY *p1, const LWPOLY *p2); char lwtriangle_same(const LWTRIANGLE *p1, const LWTRIANGLE *p2); char lwcollection_same(const LWCOLLECTION *p1, const LWCOLLECTION *p2); char lwcircstring_same(const LWCIRCSTRING *p1, const LWCIRCSTRING *p2); /* * Shift */ void ptarray_longitude_shift(POINTARRAY *pa); /* * Support for in place modification of point arrays, fast * function to move coordinate values around */ void ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to); /* * Reverse */ void ptarray_reverse_in_place(POINTARRAY *pa); /* * Startpoint */ int lwpoly_startpoint(const LWPOLY* lwpoly, POINT4D* pt); int ptarray_startpoint(const POINTARRAY* pa, POINT4D* pt); int lwcollection_startpoint(const LWCOLLECTION* col, POINT4D* pt); /* * Write into *ret the coordinates of the closest point on * segment A-B to the reference input point R */ void closest_point_on_segment(const POINT4D *R, const POINT4D *A, const POINT4D *B, POINT4D *ret); /* * Repeated points */ POINTARRAY *ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance); LWGEOM* lwline_remove_repeated_points(const LWLINE *in, double tolerance); void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points); /* * Closure test */ int lwline_is_closed(const LWLINE *line); int lwpoly_is_closed(const LWPOLY *poly); int lwcircstring_is_closed(const LWCIRCSTRING *curve); int lwcompound_is_closed(const LWCOMPOUND *curve); int lwpsurface_is_closed(const LWPSURFACE *psurface); int lwtin_is_closed(const LWTIN *tin); /** * Snap to grid */ void ptarray_grid_in_place(POINTARRAY *pa, const gridspec *grid); /* * What side of the line formed by p1 and p2 does q fall? * Returns -1 for left and 1 for right and 0 for co-linearity */ int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q); int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q); int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox); double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result); int lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2); int lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3); int lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3); double lw_seg_length(const POINT2D *A1, const POINT2D *A2); double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3); int pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring); int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt); int ptarrayarc_contains_point(const POINTARRAY *pa, const POINT2D *pt); int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number); int ptarrayarc_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number); int lwcompound_contains_point(const LWCOMPOUND *comp, const POINT2D *pt); int lwgeom_contains_point(const LWGEOM *geom, const POINT2D *pt); /** * Split a line by a point and push components to the provided multiline. * If the point doesn't split the line, push nothing to the container. * Returns 0 if the point is off the line. * Returns 1 if the point is on the line boundary (endpoints). * Return 2 if the point is on the interior of the line (only case in which * a split happens). * * NOTE: the components pushed to the output vector have their SRID stripped */ int lwline_split_by_point_to(const LWLINE* ln, const LWPOINT* pt, LWMLINE* to); /** Ensure the collection can hold at least up to ngeoms geometries */ void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms); /** Check if subtype is allowed in collectiontype */ int lwcollection_allows_subtype(int collectiontype, int subtype); /** GBOX utility functions to figure out coverage/location on the globe */ double gbox_angular_height(const GBOX* gbox); double gbox_angular_width(const GBOX* gbox); int gbox_centroid(const GBOX* gbox, POINT2D* out); /* Utilities */ int lwprint_double(double d, int maxdd, char* buf, size_t bufsize); extern uint8_t MULTITYPE[NUMTYPES]; extern lwinterrupt_callback *_lwgeom_interrupt_callback; extern int _lwgeom_interrupt_requested; #define LW_ON_INTERRUPT(x) { \ if ( _lwgeom_interrupt_callback ) { \ (*_lwgeom_interrupt_callback)(); \ } \ if ( _lwgeom_interrupt_requested ) { \ _lwgeom_interrupt_requested = 0; \ lwnotice("liblwgeom code interrupted"); \ x; \ } \ } int ptarray_npoints_in_rect(const POINTARRAY *pa, const GBOX *gbox); int gbox_contains_point2d(const GBOX *g, const POINT2D *p); int lwpoly_contains_point(const LWPOLY *poly, const POINT2D *pt); POINT4D* lwmpoint_extract_points_4d(const LWMPOINT* g, uint32_t* npoints, int* input_empty); char* lwstrdup(const char* a); #endif /* _LIBLWGEOM_INTERNAL_H */ lwgeom/src/liblwgeom/lwline.c0000644000176200001440000003720213773172540015767 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic LWLINE functions */ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" /* * Construct a new LWLINE. points will *NOT* be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points) { LWLINE *result = (LWLINE *)lwalloc(sizeof(LWLINE)); result->type = LINETYPE; result->flags = points->flags; FLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->points = points; result->bbox = bbox; return result; } LWLINE * lwline_construct_empty(int32_t srid, char hasz, char hasm) { LWLINE *result = lwalloc(sizeof(LWLINE)); result->type = LINETYPE; result->flags = lwflags(hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(hasz, hasm, 1); result->bbox = NULL; return result; } void lwline_free (LWLINE *line) { if ( ! line ) return; if ( line->bbox ) lwfree(line->bbox); if ( line->points ) ptarray_free(line->points); lwfree(line); } void printLWLINE(LWLINE *line) { lwnotice("LWLINE {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(line->flags)); lwnotice(" srid = %i", (int)line->srid); printPA(line->points); lwnotice("}"); } /* @brief Clone LWLINE object. Serialized point lists are not copied. * * @see ptarray_clone */ LWLINE * lwline_clone(const LWLINE *g) { LWLINE *ret = lwalloc(sizeof(LWLINE)); LWDEBUGF(2, "lwline_clone called with %p", g); memcpy(ret, g, sizeof(LWLINE)); ret->points = ptarray_clone(g->points); if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); return ret; } /* Deep clone LWLINE object. POINTARRAY *is* copied. */ LWLINE * lwline_clone_deep(const LWLINE *g) { LWLINE *ret = lwalloc(sizeof(LWLINE)); LWDEBUGF(2, "lwline_clone_deep called with %p", g); memcpy(ret, g, sizeof(LWLINE)); if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); if ( g->points ) ret->points = ptarray_clone_deep(g->points); FLAGS_SET_READONLY(ret->flags,0); return ret; } void lwline_release(LWLINE *lwline) { lwgeom_release(lwline_as_lwgeom(lwline)); } LWLINE * lwline_segmentize2d(const LWLINE *line, double dist) { POINTARRAY *segmentized = ptarray_segmentize2d(line->points, dist); if ( ! segmentized ) return NULL; return lwline_construct(line->srid, NULL, segmentized); } /* check coordinate equality */ char lwline_same(const LWLINE *l1, const LWLINE *l2) { return ptarray_same(l1->points, l2->points); } /* * Construct a LWLINE from an array of point and line geometries * LWLINE dimensions are large enough to host all input dimensions. */ LWLINE * lwline_from_lwgeom_array(int32_t srid, uint32_t ngeoms, LWGEOM **geoms) { uint32_t i; int hasz = LW_FALSE; int hasm = LW_FALSE; POINTARRAY *pa; LWLINE *line; POINT4D pt; LWPOINTITERATOR* it; /* * Find output dimensions, check integrity */ for (i=0; iflags) ) hasz = LW_TRUE; if ( FLAGS_GET_M(geoms[i]->flags) ) hasm = LW_TRUE; if ( hasz && hasm ) break; /* Nothing more to learn! */ } /* * ngeoms should be a guess about how many points we have in input. * It's an underestimate for lines and multipoints */ pa = ptarray_construct_empty(hasz, hasm, ngeoms); for ( i=0; i < ngeoms; i++ ) { LWGEOM *g = geoms[i]; if ( lwgeom_is_empty(g) ) continue; if ( g->type == POINTTYPE ) { lwpoint_getPoint4d_p((LWPOINT*)g, &pt); ptarray_append_point(pa, &pt, LW_TRUE); } else if ( g->type == LINETYPE ) { /* * Append the new line points, de-duplicating against the previous points. * Duplicated points internal to the linestring are untouched. */ ptarray_append_ptarray(pa, ((LWLINE*)g)->points, -1); } else if ( g->type == MULTIPOINTTYPE ) { it = lwpointiterator_create(g); while(lwpointiterator_next(it, &pt)) { ptarray_append_point(pa, &pt, LW_TRUE); } lwpointiterator_destroy(it); } else { ptarray_free(pa); lwerror("lwline_from_ptarray: invalid input type: %s", lwtype_name(g->type)); return NULL; } } if ( pa->npoints > 0 ) line = lwline_construct(srid, NULL, pa); else { /* Is this really any different from the above ? */ ptarray_free(pa); line = lwline_construct_empty(srid, hasz, hasm); } return line; } /* * Construct a LWLINE from an array of LWPOINTs * LWLINE dimensions are large enough to host all input dimensions. */ LWLINE * lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points) { uint32_t i; int hasz = LW_FALSE; int hasm = LW_FALSE; POINTARRAY *pa; LWLINE *line; POINT4D pt; /* * Find output dimensions, check integrity */ for (i=0; itype != POINTTYPE ) { lwerror("lwline_from_ptarray: invalid input type: %s", lwtype_name(points[i]->type)); return NULL; } if ( FLAGS_GET_Z(points[i]->flags) ) hasz = LW_TRUE; if ( FLAGS_GET_M(points[i]->flags) ) hasm = LW_TRUE; if ( hasz && hasm ) break; /* Nothing more to learn! */ } pa = ptarray_construct_empty(hasz, hasm, npoints); for ( i=0; i < npoints; i++ ) { if ( ! lwpoint_is_empty(points[i]) ) { lwpoint_getPoint4d_p(points[i], &pt); ptarray_append_point(pa, &pt, LW_TRUE); } } if ( pa->npoints > 0 ) line = lwline_construct(srid, NULL, pa); else line = lwline_construct_empty(srid, hasz, hasm); return line; } /* * Construct a LWLINE from a LWMPOINT */ LWLINE * lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint) { uint32_t i; POINTARRAY *pa = NULL; LWGEOM *lwgeom = (LWGEOM*)mpoint; POINT4D pt; char hasz = lwgeom_has_z(lwgeom); char hasm = lwgeom_has_m(lwgeom); uint32_t npoints = mpoint->ngeoms; if ( lwgeom_is_empty(lwgeom) ) { return lwline_construct_empty(srid, hasz, hasm); } pa = ptarray_construct(hasz, hasm, npoints); for (i=0; i < npoints; i++) { getPoint4d_p(mpoint->geoms[i]->point, 0, &pt); ptarray_set_point4d(pa, i, &pt); } LWDEBUGF(3, "lwline_from_lwmpoint: constructed pointarray for %d points", mpoint->ngeoms); return lwline_construct(srid, NULL, pa); } /** * Returns freshly allocated #LWPOINT that corresponds to the index where. * Returns NULL if the geometry is empty or the index invalid. */ LWPOINT* lwline_get_lwpoint(const LWLINE *line, uint32_t where) { POINT4D pt; LWPOINT *lwpoint; POINTARRAY *pa; if ( lwline_is_empty(line) || where >= line->points->npoints ) return NULL; pa = ptarray_construct_empty(FLAGS_GET_Z(line->flags), FLAGS_GET_M(line->flags), 1); pt = getPoint4d(line->points, where); ptarray_append_point(pa, &pt, LW_TRUE); lwpoint = lwpoint_construct(line->srid, NULL, pa); return lwpoint; } int lwline_add_lwpoint(LWLINE *line, LWPOINT *point, uint32_t where) { POINT4D pt; getPoint4d_p(point->point, 0, &pt); if ( ptarray_insert_point(line->points, &pt, where) != LW_SUCCESS ) return LW_FAILURE; /* Update the bounding box */ if ( line->bbox ) { lwgeom_refresh_bbox((LWGEOM*)line); } return LW_SUCCESS; } LWLINE * lwline_removepoint(LWLINE *line, uint32_t index) { POINTARRAY *newpa; LWLINE *ret; newpa = ptarray_removePoint(line->points, index); ret = lwline_construct(line->srid, NULL, newpa); lwgeom_add_bbox((LWGEOM *) ret); return ret; } /* * Note: input will be changed, make sure you have permissions for this. */ void lwline_setPoint4d(LWLINE *line, uint32_t index, POINT4D *newpoint) { ptarray_set_point4d(line->points, index, newpoint); /* Update the box, if there is one to update */ if ( line->bbox ) { lwgeom_refresh_bbox((LWGEOM*)line); } } /** * Re-write the measure ordinate (or add one, if it isn't already there) interpolating * the measure between the supplied start and end values. */ LWLINE* lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end) { int i = 0; int hasm = 0, hasz = 0; int npoints = 0; double length = 0.0; double length_so_far = 0.0; double m_range = m_end - m_start; double m; POINTARRAY *pa = NULL; POINT3DZ p1, p2; if ( lwline->type != LINETYPE ) { lwerror("lwline_construct_from_lwline: only line types supported"); return NULL; } hasz = FLAGS_GET_Z(lwline->flags); hasm = 1; /* Null points or npoints == 0 will result in empty return geometry */ if ( lwline->points ) { npoints = lwline->points->npoints; length = ptarray_length_2d(lwline->points); getPoint3dz_p(lwline->points, 0, &p1); } pa = ptarray_construct(hasz, hasm, npoints); for ( i = 0; i < npoints; i++ ) { POINT4D q; POINT2D a, b; getPoint3dz_p(lwline->points, i, &p2); a.x = p1.x; a.y = p1.y; b.x = p2.x; b.y = p2.y; length_so_far += distance2d_pt_pt(&a, &b); if ( length > 0.0 ) m = m_start + m_range * length_so_far / length; /* #3172, support (valid) zero-length inputs */ else if ( length == 0.0 && npoints > 1 ) m = m_start + m_range * i / (npoints-1); else m = 0.0; q.x = p2.x; q.y = p2.y; q.z = p2.z; q.m = m; ptarray_set_point4d(pa, i, &q); p1 = p2; } return lwline_construct(lwline->srid, NULL, pa); } LWGEOM* lwline_remove_repeated_points(const LWLINE *lwline, double tolerance) { return lwgeom_remove_repeated_points((LWGEOM*)lwline, tolerance); } int lwline_is_closed(const LWLINE *line) { if (FLAGS_GET_Z(line->flags)) return ptarray_is_closed_3d(line->points); return ptarray_is_closed_2d(line->points); } int lwline_is_trajectory(const LWLINE *line) { POINT3DM p; int i, n; double m = -1 * FLT_MAX; if ( ! FLAGS_GET_M(line->flags) ) { lwnotice("Line does not have M dimension"); return LW_FALSE; } n = line->points->npoints; if ( n < 2 ) return LW_TRUE; /* empty or single-point are "good" */ for (i=0; ipoints, i, &p); if ( p.m <= m ) { lwnotice("Measure of vertex %d (%g) not bigger than measure of vertex %d (%g)", i, p.m, i-1, m); return LW_FALSE; } m = p.m; } return LW_TRUE; } LWLINE* lwline_force_dims(const LWLINE *line, int hasz, int hasm) { POINTARRAY *pdims = NULL; LWLINE *lineout; /* Return 2D empty */ if( lwline_is_empty(line) ) { lineout = lwline_construct_empty(line->srid, hasz, hasm); } else { pdims = ptarray_force_dims(line->points, hasz, hasm); lineout = lwline_construct(line->srid, NULL, pdims); } lineout->type = line->type; return lineout; } uint32_t lwline_count_vertices(LWLINE *line) { assert(line); if ( ! line->points ) return 0; return line->points->npoints; } double lwline_length(const LWLINE *line) { if ( lwline_is_empty(line) ) return 0.0; return ptarray_length(line->points); } double lwline_length_2d(const LWLINE *line) { if ( lwline_is_empty(line) ) return 0.0; return ptarray_length_2d(line->points); } POINTARRAY* lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat) { POINT4D pt; uint32_t i; uint32_t points_to_interpolate; uint32_t points_found = 0; double length; double length_fraction_increment = length_fraction; double length_fraction_consumed = 0; char has_z = (char) lwgeom_has_z(lwline_as_lwgeom(line)); char has_m = (char) lwgeom_has_m(lwline_as_lwgeom(line)); const POINTARRAY* ipa = line->points; POINTARRAY* opa; /* Empty.InterpolatePoint == Point Empty */ if ( lwline_is_empty(line) ) { return ptarray_construct_empty(has_z, has_m, 0); } /* If distance is one of the two extremes, return the point on that * end rather than doing any computations */ if ( length_fraction == 0.0 || length_fraction == 1.0 ) { if ( length_fraction == 0.0 ) getPoint4d_p(ipa, 0, &pt); else getPoint4d_p(ipa, ipa->npoints-1, &pt); opa = ptarray_construct(has_z, has_m, 1); ptarray_set_point4d(opa, 0, &pt); return opa; } /* Interpolate points along the line */ length = ptarray_length_2d(ipa); points_to_interpolate = repeat ? (uint32_t) floor(1 / length_fraction) : 1; opa = ptarray_construct(has_z, has_m, points_to_interpolate); const POINT2D* p1 = getPoint2d_cp(ipa, 0); for ( i = 0; i < ipa->npoints - 1 && points_found < points_to_interpolate; i++ ) { const POINT2D* p2 = getPoint2d_cp(ipa, i+1); double segment_length_frac = distance2d_pt_pt(p1, p2) / length; /* If our target distance is before the total length we've seen * so far. create a new point some distance down the current * segment. */ while ( length_fraction < length_fraction_consumed + segment_length_frac && points_found < points_to_interpolate ) { POINT4D p1_4d = getPoint4d(ipa, i); POINT4D p2_4d = getPoint4d(ipa, i+1); double segment_fraction = (length_fraction - length_fraction_consumed) / segment_length_frac; interpolate_point4d(&p1_4d, &p2_4d, &pt, segment_fraction); ptarray_set_point4d(opa, points_found++, &pt); length_fraction += length_fraction_increment; } length_fraction_consumed += segment_length_frac; p1 = p2; } /* Return the last point on the line. This shouldn't happen, but * could if there's some floating point rounding errors. */ if (points_found < points_to_interpolate) { getPoint4d_p(ipa, ipa->npoints - 1, &pt); ptarray_set_point4d(opa, points_found, &pt); } return opa; } extern LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance) { double length, slength, tlength; POINTARRAY *ipa; POINT4D pt; int nsegs, i; LWGEOM *geom = lwline_as_lwgeom(line); int has_z = lwgeom_has_z(geom); int has_m = lwgeom_has_m(geom); ipa = line->points; /* Empty.InterpolatePoint == Point Empty */ if (lwline_is_empty(line)) { return lwpoint_construct_empty(line->srid, has_z, has_m); } /* If distance is one of the two extremes, return the point on that * end rather than doing any expensive computations */ if (distance == 0.0 || distance == 1.0) { if (distance == 0.0) getPoint4d_p(ipa, 0, &pt); else getPoint4d_p(ipa, ipa->npoints - 1, &pt); return lwpoint_make(line->srid, has_z, has_m, &pt); } /* Interpolate a point on the line */ nsegs = ipa->npoints - 1; length = ptarray_length(ipa); tlength = 0; for (i = 0; i < nsegs; i++) { POINT4D p1, p2; POINT4D *p1ptr = &p1, *p2ptr = &p2; /* don't break * strict-aliasing rules */ getPoint4d_p(ipa, i, &p1); getPoint4d_p(ipa, i + 1, &p2); /* Find the relative length of this segment */ slength = distance3d_pt_pt((POINT3D *)p1ptr, (POINT3D *)p2ptr) / length; /* If our target distance is before the total length we've seen * so far. create a new point some distance down the current * segment. */ if (distance < tlength + slength) { double dseg = (distance - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); return lwpoint_make(line->srid, has_z, has_m, &pt); } tlength += slength; } /* Return the last point on the line. This shouldn't happen, but * could if there's some floating point rounding errors. */ getPoint4d_p(ipa, ipa->npoints - 1, &pt); return lwpoint_make(line->srid, has_z, has_m, &pt); } lwgeom/src/liblwgeom/lwmsurface.c0000644000176200001440000000211413773172540016637 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" lwgeom/src/liblwgeom/lwrandom.c0000644000176200001440000000577113773172540016326 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2019 Mike Taves * **********************************************************************/ #include "lwrandom.h" #include #include #include #include #ifdef _WIN32 #include #define getpid _getpid #else #include #endif static unsigned char _lwrandom_seed_set = 0; static int32_t _lwrandom_seed[3] = {0x330e, 0xabcd, 0x1234}; /* * Set seed for a random number generator. * Repeatable numbers are generated with seed values >= 1. * When seed is zero and has not previously been set, it is based on * Unix time (seconds) and process ID. */ void lwrandom_set_seed(int32_t seed) { if (seed == 0) { if (_lwrandom_seed_set == 0) seed = (unsigned int)time(NULL) + (unsigned int)getpid() - 0xbadd; else return; } /* s1 value between 1 and 2147483562 */ _lwrandom_seed[1] = (((int64_t)seed + 0xfeed) % 2147483562) + 1; /* s2 value between 1 and 2147483398 */ _lwrandom_seed[2] = ((((int64_t)seed + 0xdefeb) << 5) % 2147483398) + 1; _lwrandom_seed_set = 1; } /* for low-level external debugging */ void _lwrandom_set_seeds(int32_t s1, int32_t s2) { /* _lwrandom_seed[0] not used */ _lwrandom_seed[1] = s1; _lwrandom_seed[2] = s2; _lwrandom_seed_set = 1; } int32_t _lwrandom_get_seed(size_t idx) { return _lwrandom_seed[idx]; } /* * Generate a random floating-point value. * Values are uniformly distributed between 0 and 1. * * Authors: * Pierre L'Ecuyer (1988), see source code in Figure 3. * C version by John Burkardt, modified by Mike Taves. * * Reference: * Pierre L'Ecuyer, * Efficient and Portable Combined Random Number Generators, * Communications of the ACM, Volume 31, Number 6, June 1988, * pages 742-751. doi:10.1145/62959.62969 */ double lwrandom_uniform(void) { double value; int32_t k; int32_t z; int32_t *s1 = &_lwrandom_seed[1]; int32_t *s2 = &_lwrandom_seed[2]; k = *s1 / 53668; *s1 = 40014 * (*s1 - k * 53668) - k * 12211; if (*s1 < 0) *s1 += 2147483563; k = *s2 / 52774; *s2 = 40692 * (*s2 - k * 52774) - k * 3791; if (*s2 < 0) *s2 += 2147483399; z = *s1 - *s2; if (z < 1) z += 2147483562; value = (double)(z) / 2147483563.0; return value; } lwgeom/src/liblwgeom/stringbuffer.c0000644000176200001440000001617213773172540017200 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2002 Thamer Alharbash * Copyright 2009 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "stringbuffer.h" /** * Allocate a new stringbuffer_t. Use stringbuffer_destroy to free. */ stringbuffer_t* stringbuffer_create(void) { return stringbuffer_create_with_size(STRINGBUFFER_STARTSIZE); } static void stringbuffer_init_with_size(stringbuffer_t *s, size_t size) { s->str_start = lwalloc(size); s->str_end = s->str_start; s->capacity = size; memset(s->str_start, 0, size); } void stringbuffer_release(stringbuffer_t *s) { if ( s->str_start ) lwfree(s->str_start); } void stringbuffer_init(stringbuffer_t *s) { stringbuffer_init_with_size(s, STRINGBUFFER_STARTSIZE); } /** * Allocate a new stringbuffer_t. Use stringbuffer_destroy to free. */ stringbuffer_t* stringbuffer_create_with_size(size_t size) { stringbuffer_t *s; s = lwalloc(sizeof(stringbuffer_t)); stringbuffer_init_with_size(s, size); return s; } /** * Free the stringbuffer_t and all memory managed within it. */ void stringbuffer_destroy(stringbuffer_t *s) { stringbuffer_release(s); if ( s ) lwfree(s); } /** * Reset the stringbuffer_t. Useful for starting a fresh string * without the expense of freeing and re-allocating a new * stringbuffer_t. */ void stringbuffer_clear(stringbuffer_t *s) { s->str_start[0] = '\0'; s->str_end = s->str_start; } /** * Return the last character in the buffer. */ char stringbuffer_lastchar(stringbuffer_t *s) { if( s->str_end == s->str_start ) return 0; return *(s->str_end - 1); } /** * Returns a reference to the internal string being managed by * the stringbuffer. The current string will be null-terminated * within the internal string. */ const char* stringbuffer_getstring(stringbuffer_t *s) { return s->str_start; } /** * Returns a newly allocated string large enough to contain the * current state of the string. Caller is responsible for * freeing the return value. */ char* stringbuffer_getstringcopy(stringbuffer_t *s) { size_t size = (s->str_end - s->str_start) + 1; char *str = lwalloc(size); memcpy(str, s->str_start, size); str[size - 1] = '\0'; return str; } /** * Returns the length of the current string, not including the * null terminator (same behavior as strlen()). */ int stringbuffer_getlength(stringbuffer_t *s) { return (s->str_end - s->str_start); } /** * Clear the stringbuffer_t and re-start it with the specified string. */ void stringbuffer_set(stringbuffer_t *s, const char *str) { stringbuffer_clear(s); stringbuffer_append(s, str); } /** * Copy the contents of src into dst. */ void stringbuffer_copy(stringbuffer_t *dst, stringbuffer_t *src) { stringbuffer_set(dst, stringbuffer_getstring(src)); } /** * Appends a formatted string to the current string buffer, * using the format and argument list provided. Returns -1 on error, * check errno for reasons, documented in the printf man page. */ static int stringbuffer_avprintf(stringbuffer_t *s, const char *fmt, va_list ap) { int maxlen = (s->capacity - (s->str_end - s->str_start)); int len = 0; /* Length of the output */ va_list ap2; /* Make a copy of the variadic arguments, in case we need to print twice */ /* Print to our buffer */ va_copy(ap2, ap); len = vsnprintf(s->str_end, maxlen, fmt, ap2); va_end(ap2); /* Propogate errors up */ if ( len < 0 ) #if defined(__MINGW64_VERSION_MAJOR) len = _vscprintf(fmt, ap2);/**Assume windows flaky vsnprintf that returns -1 if initial buffer to small and add more space **/ #else return len; #endif /* We didn't have enough space! */ /* Either Unix vsnprint returned write length larger than our buffer */ /* or Windows vsnprintf returned an error code. */ if ( len >= maxlen ) { stringbuffer_makeroom(s, len + 1); maxlen = (s->capacity - (s->str_end - s->str_start)); /* Try to print a second time */ len = vsnprintf(s->str_end, maxlen, fmt, ap); /* Printing error? Error! */ if ( len < 0 ) return len; /* Too long still? Error! */ if ( len >= maxlen ) return -1; } /* Move end pointer forward and return. */ s->str_end += len; return len; } /** * Appends a formatted string to the current string buffer, * using the format and argument list provided. * Returns -1 on error, check errno for reasons, * as documented in the printf man page. */ int stringbuffer_aprintf(stringbuffer_t *s, const char *fmt, ...) { int r; va_list ap; va_start(ap, fmt); r = stringbuffer_avprintf(s, fmt, ap); va_end(ap); return r; } /** * Trims whitespace off the end of the stringbuffer. Returns * the number of characters trimmed. */ int stringbuffer_trim_trailing_white(stringbuffer_t *s) { char *ptr = s->str_end; int dist = 0; /* Roll backwards until we hit a non-space. */ while( ptr > s->str_start ) { ptr--; if( (*ptr == ' ') || (*ptr == '\t') ) { continue; } else { ptr++; dist = s->str_end - ptr; *ptr = '\0'; s->str_end = ptr; return dist; } } return dist; } /** * Trims zeroes off the end of the last number in the stringbuffer. * The number has to be the very last thing in the buffer. Only the * last number will be trimmed. Returns the number of characters * trimmed. * * eg: 1.22000 -> 1.22 * 1.0 -> 1 * 0.0 -> 0 */ int stringbuffer_trim_trailing_zeroes(stringbuffer_t *s) { char *ptr = s->str_end; char *decimal_ptr = NULL; int dist; if ( s->str_end - s->str_start < 2) return 0; /* Roll backwards to find the decimal for this number */ while( ptr > s->str_start ) { ptr--; if ( *ptr == '.' ) { decimal_ptr = ptr; break; } if ( (*ptr >= '0') && (*ptr <= '9' ) ) continue; else break; } /* No decimal? Nothing to trim! */ if ( ! decimal_ptr ) return 0; ptr = s->str_end; /* Roll backwards again, with the decimal as stop point, trimming contiguous zeroes */ while( ptr >= decimal_ptr ) { ptr--; if ( *ptr == '0' ) continue; else break; } /* Huh, we get anywhere. Must not have trimmed anything. */ if ( ptr == s->str_end ) return 0; /* If we stopped at the decimal, we want to null that out. It we stopped on a numeral, we want to preserve that, so push the pointer forward one space. */ if ( *ptr != '.' ) ptr++; /* Add null terminator re-set the end of the stringbuffer. */ *ptr = '\0'; dist = s->str_end - ptr; s->str_end = ptr; return dist; } lwgeom/src/liblwgeom/liblwgeom_topo.h0000644000176200001440000013231613773172540017526 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2015-2017 Sandro Santilli * **********************************************************************/ #ifndef LIBLWGEOM_TOPO_H #define LIBLWGEOM_TOPO_H 1 #include "liblwgeom.h" /* INT64 */ typedef int64_t LWT_INT64; /** Identifier of topology element */ typedef LWT_INT64 LWT_ELEMID; /* * ISO primitive elements */ /** NODE */ typedef struct { LWT_ELEMID node_id; LWT_ELEMID containing_face; /* -1 if not isolated */ LWPOINT *geom; } LWT_ISO_NODE; void lwt_iso_node_release(LWT_ISO_NODE* node); /** Node fields */ #define LWT_COL_NODE_NODE_ID 1<<0 #define LWT_COL_NODE_CONTAINING_FACE 1<<1 #define LWT_COL_NODE_GEOM 1<<2 #define LWT_COL_NODE_ALL (1<<3)-1 /** EDGE */ typedef struct { LWT_ELEMID edge_id; LWT_ELEMID start_node; LWT_ELEMID end_node; LWT_ELEMID face_left; LWT_ELEMID face_right; LWT_ELEMID next_left; LWT_ELEMID next_right; LWLINE *geom; } LWT_ISO_EDGE; /** Edge fields */ #define LWT_COL_EDGE_EDGE_ID 1<<0 #define LWT_COL_EDGE_START_NODE 1<<1 #define LWT_COL_EDGE_END_NODE 1<<2 #define LWT_COL_EDGE_FACE_LEFT 1<<3 #define LWT_COL_EDGE_FACE_RIGHT 1<<4 #define LWT_COL_EDGE_NEXT_LEFT 1<<5 #define LWT_COL_EDGE_NEXT_RIGHT 1<<6 #define LWT_COL_EDGE_GEOM 1<<7 #define LWT_COL_EDGE_ALL (1<<8)-1 /** FACE */ typedef struct { LWT_ELEMID face_id; GBOX *mbr; } LWT_ISO_FACE; /** Face fields */ #define LWT_COL_FACE_FACE_ID 1<<0 #define LWT_COL_FACE_MBR 1<<1 #define LWT_COL_FACE_ALL (1<<2)-1 typedef enum LWT_SPATIALTYPE_T { LWT_PUNTAL = 0, LWT_LINEAL = 1, LWT_AREAL = 2, LWT_COLLECTION = 3 } LWT_SPATIALTYPE; /* * Backend handling functions */ /* opaque pointers referencing native backend objects */ /** * Backend private data pointer * * Only the backend handler needs to know what it really is. * It will be passed to all registered callback functions. */ typedef struct LWT_BE_DATA_T LWT_BE_DATA; /** * Backend interface handler * * Embeds all registered backend callbacks and private data pointer. * Will need to be passed (directly or indirectly) to al public facing * APIs of this library. */ typedef struct LWT_BE_IFACE_T LWT_BE_IFACE; /** * Topology handler. * * Embeds backend interface handler. * Will need to be passed to all topology manipulation APIs * of this library. */ typedef struct LWT_BE_TOPOLOGY_T LWT_BE_TOPOLOGY; /** * Structure containing base backend callbacks * * Used for registering into the backend iface */ typedef struct LWT_BE_CALLBACKS_T { /** * Read last error message from backend * * @return NULL-terminated error string */ const char* (*lastErrorMessage) (const LWT_BE_DATA* be); /** * Create a new topology in the backend * * @param name the topology name * @param srid the topology SRID * @param precision the topology precision/tolerance * @param hasZ non-zero if topology primitives should have a Z ordinate * @return a topology handler, which embeds the backend data/params * or NULL on error (@see lastErrorMessage) */ LWT_BE_TOPOLOGY *(*createTopology)(const LWT_BE_DATA *be, const char *name, int32_t srid, double precision, int hasZ); /** * Load a topology from the backend * * @param name the topology name * @return a topology handler, which embeds the backend data/params * or NULL on error (@see lastErrorMessage) */ LWT_BE_TOPOLOGY* (*loadTopologyByName) ( const LWT_BE_DATA* be, const char* name ); /** * Release memory associated to a backend topology * * @param topo the backend topology handler * @return 1 on success, 0 on error (@see lastErrorMessage) */ int (*freeTopology) (LWT_BE_TOPOLOGY* topo); /** * Get nodes by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of node identifiers * in the input array, gets number of node in output array. * @param fields fields to be filled in the returned structure, see * LWT_COL_NODE_* macros * * @return an array of nodes * or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) * (@see lastErrorMessage) * */ LWT_ISO_NODE *(*getNodeById)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); /** * Get nodes within distance by point * * @param topo the topology to act upon * @param pt the query point * @param dist the distance * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_NODE_* macros * @param limit max number of nodes to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of nodes or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ LWT_ISO_NODE *(*getNodeWithinDistance2D)(const LWT_BE_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit); /** * Insert nodes * * Insert node primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param nodes the nodes to insert. Those with a node_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the nodes array * * @return 1 on success, 0 on error (@see lastErrorMessage) */ int (*insertNodes)(const LWT_BE_TOPOLOGY *topo, LWT_ISO_NODE *nodes, uint64_t numelems); /** * Get edge by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of edge identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_EDGE_* macros * * @return an array of edges or NULL in the following cases: * - none found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ LWT_ISO_EDGE *(*getEdgeById)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); /** * Get edges within distance by point * * @param topo the topology to act upon * @param pt the query point * @param dist the distance * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_EDGE_* macros * @param limit max number of edges to return, 0 for no limit, -1 * to only check for existence if a matching row. * * @return an array of edges or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ LWT_ISO_EDGE *(*getEdgeWithinDistance2D)(const LWT_BE_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit); /** * Get next available edge identifier * * Identifiers returned by this function should not be considered * available anymore. * * @param topo the topology to act upon * * @return next available edge identifier or -1 on error */ LWT_ELEMID (*getNextEdgeId) ( const LWT_BE_TOPOLOGY* topo ); /** * Insert edges * * Insert edge primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param edges the edges to insert. Those with a edge_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the edges array * * @return number of inserted edges, or -1 (@see lastErrorMessage) */ int (*insertEdges)(const LWT_BE_TOPOLOGY *topo, LWT_ISO_EDGE *edges, uint64_t numelems); /** * Update edges selected by fields match/mismatch * * @param topo the topology to act upon * @param sel_edge an LWT_ISO_EDGE object with selecting fields set. * @param sel_fields fields used to select edges to be updated, * see LWT_COL_EDGE_* macros * @param upd_edge an LWT_ISO_EDGE object with updated fields set. * @param upd_fields fields to be updated for the selected edges, * see LWT_COL_EDGE_* macros * @param exc_edge an LWT_ISO_EDGE object with exclusion fields set, * can be NULL if no exlusion condition exists. * @param exc_fields fields used for excluding edges from the update, * see LWT_COL_EDGE_* macros * * @return number of edges being updated or -1 on error * (@see lastErroMessage) */ int (*updateEdges) ( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields, const LWT_ISO_EDGE* upd_edge, int upd_fields, const LWT_ISO_EDGE* exc_edge, int exc_fields ); /** * Get faces by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of edge identifiers * in the input array, gets number of node in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_FACE_* macros * * @return an array of faces or NULL in the following cases: * - none found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ LWT_ISO_FACE *(*getFaceById)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); /** * Get face containing point * * @param topo the topology to act upon * @param pt the query point * * @return a face identifier, -1 if no face contains the point * (could be in universe face or on an edge) * or -2 on error (@see lastErrorMessage) */ LWT_ELEMID (*getFaceContainingPoint) ( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt ); /** * Update TopoGeometry objects after an edge split event * * @param topo the topology to act upon * @param split_edge identifier of the edge that was split. * @param new_edge1 identifier of the first new edge that was created * as a result of edge splitting. * @param new_edge2 identifier of the second new edge that was created * as a result of edge splitting, or -1 if the old edge was * modified rather than replaced. * * @return 1 on success, 0 on error * * @note on splitting an edge, the new edges both have the * same direction as the original one. If a second new edge was * created, its start node will be equal to the first new edge * end node. */ int (*updateTopoGeomEdgeSplit) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2 ); /** * Delete edges * * @param topo the topology to act upon * @param sel_edge an LWT_ISO_EDGE object with selecting fields set. * @param sel_fields fields used to select edges to be deleted, * see LWT_COL_EDGE_* macros * * @return number of edges being deleted or -1 on error * (@see lastErroMessage) */ int (*deleteEdges) ( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields ); /** * Get edges whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_NODE_* macros * @param limit max number of nodes to return, 0 for no limit, -1 * to only check for existence if a matching row. * * @return an array of nodes or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ LWT_ISO_NODE *( *getNodeWithinBox2D)(const LWT_BE_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, int limit); /** * Get edges whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box, to be considered infinite if NULL * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_EDGE_* macros * @param limit max number of edges to return, 0 for no limit, -1 * to only check for existence if a matching row. * * @return an array of edges or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ LWT_ISO_EDGE *( *getEdgeWithinBox2D)(const LWT_BE_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, int limit); /** * Get edges that start or end on any of the given node identifiers * * @param topo the topology to act upon * @param ids an array of node identifiers * @param numelems input/output parameter, pass number of node identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_EDGE_* macros * * @return an array of edges that are incident to a node * or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) * (@see lastErrorMessage) */ LWT_ISO_EDGE *(*getEdgeByNode)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields); /** * Update nodes selected by fields match/mismatch * * @param topo the topology to act upon * @param sel_node an LWT_ISO_NODE object with selecting fields set. * @param sel_fields fields used to select nodes to be updated, * see LWT_COL_NODE_* macros * @param upd_node an LWT_ISO_NODE object with updated fields set. * @param upd_fields fields to be updated for the selected nodes, * see LWT_COL_NODE_* macros * @param exc_node an LWT_ISO_NODE object with exclusion fields set, * can be NULL if no exclusion condition exists. * @param exc_fields fields used for excluding nodes from the update, * see LWT_COL_NODE_* macros * * @return number of nodes being updated or -1 on error * (@see lastErroMessage) */ int (*updateNodes) ( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_NODE* sel_node, int sel_fields, const LWT_ISO_NODE* upd_node, int upd_fields, const LWT_ISO_NODE* exc_node, int exc_fields ); /** * Update TopoGeometry objects after a face split event * * @param topo the topology to act upon * @param split_face identifier of the face that was split. * @param new_face1 identifier of the first new face that was created * as a result of face splitting. * @param new_face2 identifier of the second new face that was created * as a result of face splitting, or -1 if the old face was * modified rather than replaced. * * @return 1 on success, 0 on error (@see lastErroMessage) * */ int (*updateTopoGeomFaceSplit) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2 ); /** * Insert faces * * Insert face primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param faces the faces to insert. Those with a node_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the faces array * * @return number of inserted faces, or -1 (@see lastErrorMessage) */ int (*insertFaces)(const LWT_BE_TOPOLOGY *topo, LWT_ISO_FACE *faces, uint64_t numelems); /** * Update faces by id * * @param topo the topology to act upon * @param faces an array of LWT_ISO_FACE object with selecting id * and setting mbr. * @param numfaces number of faces in the "faces" array * * @return number of faces being updated or UINT64_MAX on error * (@see lastErroMessage) */ uint64_t (*updateFacesById) ( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_FACE* faces, uint64_t numfaces ); /* * Get the ordered list edge visited by a side walk * * The walk starts from the side of an edge and stops when * either the max number of visited edges OR the starting * position is reached. The output list never includes a * duplicated signed edge identifier. * * It is expected that the walk uses the "next_left" and "next_right" * attributes of ISO edges to perform the walk (rather than recomputing * the turns at each node). * * @param topo the topology to operate on * @param edge walk start position and direction: * abs value identifies the edge, sign expresses * side (left if positive, right if negative) * and direction (forward if positive, backward if negative). * @param numedges output parameter, gets the number of edges visited * * @param limit max edges to return (to avoid an infinite loop in case * of a corrupted topology). 0 is for unlimited. * The function is expected to error out if the limit is hit. * * @return an array of signed edge identifiers (positive edges being * walked in their direction, negative ones in opposite) or * NULL on error (@see lastErrorMessage) */ LWT_ELEMID *(*getRingEdges)(const LWT_BE_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, int limit); /** * Update edges by id * * @param topo the topology to act upon * @param edges an array of LWT_ISO_EDGE object with selecting id * and updating fields. * @param numedges number of edges in the "edges" array * @param upd_fields fields to be updated for the selected edges, * see LWT_COL_EDGE_* macros * * @return number of edges being updated or -1 on error * (@see lastErrorMessage) */ int (*updateEdgesById)(const LWT_BE_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, uint64_t numedges, int upd_fields); /** * \brief * Get edges that have any of the given faces on the left or right side * and optionally whose bounding box overlaps the given one. * * @param topo the topology to act upon * @param ids an array of face identifiers * @param numelems input/output parameter, pass number of face identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_EDGE_* macros * @param box optional bounding box to further restrict matches, use * NULL for no further restriction. * * @return an array of edges identifiers or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ LWT_ISO_EDGE *(*getEdgeByFace)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box); /** * Get isolated nodes contained in any of the given faces * * @param topo the topology to act upon * @param faces an array of face identifiers * @param numelems input/output parameter, pass number of face * identifiers in the input array, gets number of * nodes in output array if the return is not null, * otherwise see @return section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_NODE_* macros * @param box optional bounding box to further restrict matches, use * NULL for no further restriction. * * @return an array of nodes or NULL in the following cases: * - no nod found ("numelems" is set to 0) * - error ("numelems" is set to -1, @see lastErrorMessage) */ LWT_ISO_NODE *(*getNodeByFace)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *faces, uint64_t *numelems, int fields, const GBOX *box); /** * Update nodes by id * * @param topo the topology to act upon * @param nodes an array of LWT_ISO_EDGE objects with selecting id * and updating fields. * @param numnodes number of nodes in the "nodes" array * @param upd_fields fields to be updated for the selected edges, * see LWT_COL_NODE_* macros * * @return number of nodes being updated or -1 on error * (@see lastErrorMessage) */ int (*updateNodesById)(const LWT_BE_TOPOLOGY *topo, const LWT_ISO_NODE *nodes, uint64_t numnodes, int upd_fields); /** * Delete faces by id * * @param topo the topology to act upon * @param ids an array of face identifiers * @param numelems number of face identifiers in the ids array * * @return number of faces being deleted or -1 on error * (@see lastErrorMessage) */ int (*deleteFacesById)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems); /** * Get topology SRID * @return 0 for unknown */ int (*topoGetSRID) ( const LWT_BE_TOPOLOGY* topo ); /** * Get topology precision */ double (*topoGetPrecision) ( const LWT_BE_TOPOLOGY* topo ); /** * Get topology Z flag * @return 1 if topology elements do have Z value, 0 otherwise */ int (*topoHasZ) ( const LWT_BE_TOPOLOGY* topo ); /** * Delete nodes by id * * @param topo the topology to act upon * @param ids an array of node identifiers * @param numelems number of node identifiers in the ids array * * @return number of nodes being deleted or -1 on error * (@see lastErrorMessage) */ int (*deleteNodesById)(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems); /** * Check TopoGeometry objects before an edge removal event * * @param topo the topology to act upon * @param rem_edge identifier of the edge that's been removed * @param face_left identifier of the face on the edge's left side * @param face_right identifier of the face on the edge's right side * * @return 1 to allow, 0 to forbid the operation * (reporting reason via lastErrorMessage) * */ int (*checkTopoGeomRemEdge) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID rem_edge, LWT_ELEMID face_left, LWT_ELEMID face_right ); /** * Update TopoGeometry objects after healing two faces * * @param topo the topology to act upon * @param face1 identifier of the first face * @param face2 identifier of the second face * @param newface identifier of the new face * * @note that newface may or may not be equal to face1 or face2, * while face1 should never be the same as face2. * * @return 1 on success, 0 on error (@see lastErrorMessage) * */ int (*updateTopoGeomFaceHeal) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface ); /** * Check TopoGeometry objects before a node removal event * * @param topo the topology to act upon * @param rem_node identifier of the node that's been removed * @param e1 identifier of the first connected edge * @param e2 identifier of the second connected edge * * The operation should be forbidden if any TopoGeometry object * exists which contains only one of the two healed edges. * * The operation should also be forbidden if the removed node * takes part in the definition of a TopoGeometry, although * this wasn't the case yet as of PostGIS version 2.1.8: * https://trac.osgeo.org/postgis/ticket/3239 * * @return 1 to allow, 0 to forbid the operation * (reporting reason via lastErrorMessage) * */ int (*checkTopoGeomRemNode) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID rem_node, LWT_ELEMID e1, LWT_ELEMID e2 ); /** * Update TopoGeometry objects after healing two edges * * @param topo the topology to act upon * @param edge1 identifier of the first edge * @param edge2 identifier of the second edge * @param newedge identifier of the new edge, taking the space * previously occupied by both original edges * * @note that newedge may or may not be equal to edge1 or edge2, * while edge1 should never be the same as edge2. * * @return 1 on success, 0 on error (@see lastErrorMessage) * */ int (*updateTopoGeomEdgeHeal) ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID edge1, LWT_ELEMID edge2, LWT_ELEMID newedge ); /** * Get faces whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * LWT_COL_FACE_* macros * @param limit max number of faces to return, 0 for no limit, -1 * to only check for existence if a matching row. * * @return an array of faces or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ LWT_ISO_FACE *(*getFaceWithinBox2D)(const LWT_BE_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, int limit); } LWT_BE_CALLBACKS; /** * Create a new backend interface * * Ownership to caller delete with lwt_FreeBackendIface * * @param data Backend data, passed as first parameter to all callback functions */ LWT_BE_IFACE* lwt_CreateBackendIface(const LWT_BE_DATA* data); /** * Register backend callbacks into the opaque iface handler * * @param iface the backend interface handler (see lwt_CreateBackendIface) * @param cb a pointer to the callbacks structure; ownership left to caller. */ void lwt_BackendIfaceRegisterCallbacks(LWT_BE_IFACE* iface, const LWT_BE_CALLBACKS* cb); /** Release memory associated with an LWT_BE_IFACE */ void lwt_FreeBackendIface(LWT_BE_IFACE* iface); /******************************************************************** * * End of BE interface * *******************************************************************/ /** * Topology errors type */ typedef enum LWT_TOPOERR_TYPE_T { LWT_TOPOERR_EDGE_CROSSES_NODE, LWT_TOPOERR_EDGE_INVALID, LWT_TOPOERR_EDGE_NOT_SIMPLE, LWT_TOPOERR_EDGE_CROSSES_EDGE, LWT_TOPOERR_EDGE_STARTNODE_MISMATCH, LWT_TOPOERR_EDGE_ENDNODE_MISMATCH, LWT_TOPOERR_FACE_WITHOUT_EDGES, LWT_TOPOERR_FACE_HAS_NO_RINGS, LWT_TOPOERR_FACE_OVERLAPS_FACE, LWT_TOPOERR_FACE_WITHIN_FACE } LWT_TOPOERR_TYPE; /** Topology error */ typedef struct LWT_TOPOERR_T { /** Type of error */ LWT_TOPOERR_TYPE err; /** Identifier of first affected element */ LWT_ELEMID elem1; /** Identifier of second affected element (0 if inapplicable) */ LWT_ELEMID elem2; } LWT_TOPOERR; /* * Topology functions */ /** Opaque topology structure * * Embeds backend interface and topology */ typedef struct LWT_TOPOLOGY_T LWT_TOPOLOGY; /******************************************************************* * * Non-ISO signatures here * *******************************************************************/ /** * Initializes a new topology * * @param iface the backend interface handler (see lwt_CreateBackendIface) * @param name name of the new topology * @param srid the topology SRID * @param prec the topology precision/tolerance * @param hasz non-zero if topology primitives should have a Z ordinate * * @return the handler of the topology, or NULL on error * (liblwgeom error handler will be invoked with error message) */ LWT_TOPOLOGY *lwt_CreateTopology(LWT_BE_IFACE *iface, const char *name, int32_t srid, double prec, int hasz); /** * Loads an existing topology by name from the database * * @param iface the backend interface handler (see lwt_CreateBackendIface) * @param name name of the topology to load * * @return the handler of the topology, or NULL on error * (liblwgeom error handler will be invoked with error message) */ LWT_TOPOLOGY *lwt_LoadTopology(LWT_BE_IFACE *iface, const char *name); /** * Drop a topology and all its associated objects from the database * * @param topo the topology to drop */ void lwt_DropTopology(LWT_TOPOLOGY* topo); /** Release memory associated with an LWT_TOPOLOGY * * @param topo the topology to release (it's not removed from db) */ void lwt_FreeTopology(LWT_TOPOLOGY* topo); /** * Retrieve the id of a node at a point location * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for a node * @return a node identifier if one is found, 0 if none is found, -1 * on error (multiple nodes within distance). * The liblwgeom error handler will be invoked in case of error. */ LWT_ELEMID lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol); /** * Find the edge-id of an edge that intersects a given point * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for an * intersecting edge * @return an edge identifier if one is found, 0 if none is found, -1 * on error (multiple edges within distance). * The liblwgeom error handler will be invoked in case of error. */ LWT_ELEMID lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol); /** * Find the face-id of a face containing a given point * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for a * containing face * @return a face identifier if one is found (0 if universe), -1 * on error (multiple faces within distance or point on node * or edge). * The liblwgeom error handler will be invoked in case of error. */ LWT_ELEMID lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol); /******************************************************************* * * Topology population (non-ISO) * *******************************************************************/ /** * Adds a point to the topology * * The given point will snap to existing nodes or edges within given * tolerance. An existing edge may be split by the point. * * @param topo the topology to operate on * @param point the point to add * @param tol snap tolerance, the topology tolerance will be used if 0 * * @return identifier of added (or pre-existing) node or -1 on error * (liblwgeom error handler will be invoked with error message) */ LWT_ELEMID lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol); /** * Adds a linestring to the topology * * The given line will snap to existing nodes or edges within given * tolerance. Existing edges or faces may be split by the line. * * @param topo the topology to operate on * @param line the line to add * @param tol snap tolerance, the topology tolerance will be used if 0 * @param nedges output parameter, will be set to number of edges the * line was split into, or -1 on error * (liblwgeom error handler will be invoked with error message) * * @return an array of edge identifiers that sewed togheter * will build up the input linestring (after snapping). Caller * will need to free the array using lwfree(), if not null. */ LWT_ELEMID* lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges); /** * Adds a linestring to the topology without determining generated faces * * The given line will snap to existing nodes or edges within given * tolerance. Existing edges or faces may be split by the line. * * Side faces for the new edges will not be determined and no new * faces will be created, effectively leaving the topology in an * invalid state (WARNING!) * * @param topo the topology to operate on * @param line the line to add * @param tol snap tolerance, the topology tolerance will be used if 0 * @param nedges output parameter, will be set to number of edges the * line was split into, or -1 on error * (liblwgeom error handler will be invoked with error message) * * @return an array of edge identifiers that sewed togheter * will build up the input linestring (after snapping). Caller * will need to free the array using lwfree(), if not null. */ LWT_ELEMID* lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges); /* * Determine and register all topology faces: * * - Determines which faces are generated by existing * edges. * - Creates face records with correct mbr * - Update edge left/right face attributes * * Precondition: * - the topology edges are correctly linked * - there are no faces registered in the topology * * Postconditions: * - all left/right face attributes of edges * reference faces with correct mbr. * * Notes: * - does not attempt to assign isolated nodes to their * containing faces * - does not remove existing face records * - loads in memory all the topology edges * * @param topo the topology to operate on * * @return 0 on success, -1 on error * (librtgeom error handler will be invoked with error * message) */ int lwt_Polygonize(LWT_TOPOLOGY* topo); /** * Adds a polygon to the topology * * The boundary of the given polygon will snap to existing nodes or * edges within given tolerance. * Existing edges or faces may be split by the boundary of the polygon. * * @param topo the topology to operate on * @param poly the polygon to add * @param tol snap tolerance, the topology tolerance will be used if 0 * @param nfaces output parameter, will be set to number of faces the * polygon was split into, or -1 on error * (liblwgeom error handler will be invoked with error message) * * @return an array of face identifiers that sewed togheter * will build up the input polygon (after snapping). Caller * will need to free the array using lwfree(), if not null. */ LWT_ELEMID* lwt_AddPolygon(LWT_TOPOLOGY* topo, LWPOLY* poly, double tol, int* nfaces); /******************************************************************* * * ISO signatures here * *******************************************************************/ /** * Populate an empty topology with data from a simple geometry * * For ST_CreateTopoGeo * * @param topo the topology to operate on * @param geom the geometry to import * */ void lwt_CreateTopoGeo(LWT_TOPOLOGY* topo, LWGEOM *geom); /** * Add an isolated node * * For ST_AddIsoNode * * @param topo the topology to operate on * @param face the identifier of containing face or -1 for "unknown" * @param pt the node position * @param skipChecks if non-zero skips consistency checks * (coincident nodes, crossing edges, * actual face containment) * * @return ID of the newly added node, or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_AddIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWPOINT* pt, int skipChecks); /** * Move an isolated node * * For ST_MoveIsoNode * * @param topo the topology to operate on * @param node the identifier of the nod to be moved * @param pt the new node position * @return 0 on success, -1 on error * (liblwgeom error handler will be invoked with error message) * */ int lwt_MoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node, LWPOINT* pt); /** * Remove an isolated node * * For ST_RemoveIsoNode * * @param topo the topology to operate on * @param node the identifier of the node to be moved * @return 0 on success, -1 on error * (liblwgeom error handler will be invoked with error message) * */ int lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node); /** * Remove an isolated edge * * For ST_RemIsoEdge * * @param topo the topology to operate on * @param edge the identifier of the edge to be moved * @return 0 on success, -1 on error * (liblwgeom error handler will be invoked with error message) * */ int lwt_RemIsoEdge(LWT_TOPOLOGY* topo, LWT_ELEMID edge); /** * Add an isolated edge connecting two existing isolated nodes * * For ST_AddIsoEdge * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @return ID of the newly added edge, or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_AddIsoEdge(LWT_TOPOLOGY* topo, LWT_ELEMID startNode, LWT_ELEMID endNode, const LWLINE *geom); /** * Add a new edge possibly splitting a face (modifying it) * * For ST_AddEdgeModFace * * If the new edge splits a face, the face is shrunk and a new one * is created. Unless the face being split is the Universal Face, the * new face will be on the right side of the newly added edge. * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @param skipChecks if non-zero skips consistency checks * (curve being simple and valid, start/end nodes * consistency actual face containment) * * @return ID of the newly added edge or null on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_AddEdgeModFace(LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks); /** * Add a new edge possibly splitting a face (replacing with two new faces) * * For ST_AddEdgeNewFaces * * If the new edge splits a face, the face is replaced by two new faces. * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @param skipChecks if non-zero skips consistency checks * (curve being simple and valid, start/end nodes * consistency actual face containment) * @return ID of the newly added edge * */ LWT_ELEMID lwt_AddEdgeNewFaces(LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks); /** * Remove an edge, possibly merging two faces (replacing both with a new one) * * For ST_RemEdgeNewFace * * @param topo the topology to operate on * @param edge identifier of the edge to be removed * @return the id of newly created face, 0 if no new face was created * or -1 on error * */ LWT_ELEMID lwt_RemEdgeNewFace(LWT_TOPOLOGY* topo, LWT_ELEMID edge); /** * Remove an edge, possibly merging two faces (replacing one with the other) * * For ST_RemEdgeModFace * * Preferentially keep the face on the right, to be symmetric with * lwt_AddEdgeModFace. * * @param topo the topology to operate on * @param edge identifier of the edge to be removed * @return the id of the face that takes the space previously occupied * by the removed edge, or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_RemEdgeModFace(LWT_TOPOLOGY* topo, LWT_ELEMID edge); /** * Changes the shape of an edge without affecting the topology structure. * * For ST_ChangeEdgeGeom * * @param topo the topology to operate on * @param curve the edge geometry * @return 0 on success, -1 on error * (liblwgeom error handler will be invoked with error message) * */ int lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWLINE* curve); /** * Split an edge by a node, modifying the original edge and adding a new one. * * For ST_ModEdgeSplit * * @param topo the topology to operate on * @param edge identifier of the edge to be split * @param pt geometry of the new node * @param skipChecks if non-zero skips consistency checks * (coincident node, point not on edge...) * @return the id of newly created node, or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_ModEdgeSplit(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipChecks); /** * Split an edge by a node, replacing it with two new edges * * For ST_NewEdgesSplit * * @param topo the topology to operate on * @param edge identifier of the edge to be split * @param pt geometry of the new node * @param skipChecks if non-zero skips consistency checks * (coincident node, point not on edge...) * @return the id of newly created node * */ LWT_ELEMID lwt_NewEdgesSplit(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipChecks); /** * Merge two edges, modifying the first and deleting the second * * For ST_ModEdgeHeal * * @param topo the topology to operate on * @param e1 identifier of first edge * @param e2 identifier of second edge * @return the id of the removed node or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_ModEdgeHeal(LWT_TOPOLOGY* topo, LWT_ELEMID e1, LWT_ELEMID e2); /** * Merge two edges, replacing both with a new one * * For ST_NewEdgeHeal * * @param topo the topology to operate on * @param e1 identifier of first edge * @param e2 identifier of second edge * @return the id of the new edge or -1 on error * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_NewEdgeHeal(LWT_TOPOLOGY* topo, LWT_ELEMID e1, LWT_ELEMID e2); /** * Return the list of directed edges bounding a face * * For ST_GetFaceEdges * * @param topo the topology to operate on * @param face identifier of the face * @param edges will be set to an array of signed edge identifiers, will * need to be released with lwfree * @return the number of edges in the edges array, or -1 on error * (liblwgeom error handler will be invoked with error message) * */ int lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWT_ELEMID **edges); /** * Return the geometry of a face * * For ST_GetFaceGeometry * * @param topo the topology to operate on * @param face identifier of the face * @return a polygon geometry representing the face, ownership to caller, * to be released with lwgeom_release, or NULL on error * (liblwgeom error handler will be invoked with error message) */ LWGEOM* lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID face); #endif /* LIBLWGEOM_TOPO_H */ lwgeom/src/liblwgeom/lwpoint.c0000644000176200001440000001456313773172540016176 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" /* * Convenience functions to hide the POINTARRAY * TODO: obsolete this */ int lwpoint_getPoint2d_p(const LWPOINT *point, POINT2D *out) { return lwpoint_is_empty(point) ? 0 : getPoint2d_p(point->point, 0, out); } /* convenience functions to hide the POINTARRAY */ int lwpoint_getPoint3dz_p(const LWPOINT *point, POINT3DZ *out) { return lwpoint_is_empty(point) ? 0 : getPoint3dz_p(point->point,0,out); } int lwpoint_getPoint3dm_p(const LWPOINT *point, POINT3DM *out) { return lwpoint_is_empty(point) ? 0 : getPoint3dm_p(point->point,0,out); } int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out) { return lwpoint_is_empty(point) ? 0 : getPoint4d_p(point->point,0,out); } double lwpoint_get_x(const LWPOINT *point) { POINT4D pt; if ( lwpoint_is_empty(point) ) { lwerror("lwpoint_get_x called with empty geometry"); return 0; } getPoint4d_p(point->point, 0, &pt); return pt.x; } double lwpoint_get_y(const LWPOINT *point) { POINT4D pt; if ( lwpoint_is_empty(point) ) { lwerror("lwpoint_get_y called with empty geometry"); return 0; } getPoint4d_p(point->point, 0, &pt); return pt.y; } double lwpoint_get_z(const LWPOINT *point) { POINT4D pt; if ( lwpoint_is_empty(point) ) { lwerror("lwpoint_get_z called with empty geometry"); return 0; } if ( ! FLAGS_GET_Z(point->flags) ) { lwerror("lwpoint_get_z called without z dimension"); return 0; } getPoint4d_p(point->point, 0, &pt); return pt.z; } double lwpoint_get_m(const LWPOINT *point) { POINT4D pt; if ( lwpoint_is_empty(point) ) { lwerror("lwpoint_get_m called with empty geometry"); return 0; } if ( ! FLAGS_GET_M(point->flags) ) { lwerror("lwpoint_get_m called without m dimension"); return 0; } getPoint4d_p(point->point, 0, &pt); return pt.m; } /* * Construct a new point. point will not be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point) { LWPOINT *result; lwflags_t flags = 0; if (point == NULL) return NULL; /* error */ result = lwalloc(sizeof(LWPOINT)); result->type = POINTTYPE; FLAGS_SET_Z(flags, FLAGS_GET_Z(point->flags)); FLAGS_SET_M(flags, FLAGS_GET_M(point->flags)); FLAGS_SET_BBOX(flags, bbox?1:0); result->flags = flags; result->srid = srid; result->point = point; result->bbox = bbox; return result; } LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm) { LWPOINT *result = lwalloc(sizeof(LWPOINT)); result->type = POINTTYPE; result->flags = lwflags(hasz, hasm, 0); result->srid = srid; result->point = ptarray_construct(hasz, hasm, 0); result->bbox = NULL; return result; } LWPOINT * lwpoint_make2d(int32_t srid, double x, double y) { POINT4D p = {x, y, 0.0, 0.0}; POINTARRAY *pa = ptarray_construct_empty(0, 0, 1); ptarray_append_point(pa, &p, LW_TRUE); return lwpoint_construct(srid, NULL, pa); } LWPOINT * lwpoint_make3dz(int32_t srid, double x, double y, double z) { POINT4D p = {x, y, z, 0.0}; POINTARRAY *pa = ptarray_construct_empty(1, 0, 1); ptarray_append_point(pa, &p, LW_TRUE); return lwpoint_construct(srid, NULL, pa); } LWPOINT * lwpoint_make3dm(int32_t srid, double x, double y, double m) { POINT4D p = {x, y, 0.0, m}; POINTARRAY *pa = ptarray_construct_empty(0, 1, 1); ptarray_append_point(pa, &p, LW_TRUE); return lwpoint_construct(srid, NULL, pa); } LWPOINT * lwpoint_make4d(int32_t srid, double x, double y, double z, double m) { POINT4D p = {x, y, z, m}; POINTARRAY *pa = ptarray_construct_empty(1, 1, 1); ptarray_append_point(pa, &p, LW_TRUE); return lwpoint_construct(srid, NULL, pa); } LWPOINT * lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p) { POINTARRAY *pa = ptarray_construct_empty(hasz, hasm, 1); ptarray_append_point(pa, p, LW_TRUE); return lwpoint_construct(srid, NULL, pa); } void lwpoint_free(LWPOINT *pt) { if ( ! pt ) return; if ( pt->bbox ) lwfree(pt->bbox); if ( pt->point ) ptarray_free(pt->point); lwfree(pt); } void printLWPOINT(LWPOINT *point) { lwnotice("LWPOINT {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(point->flags)); lwnotice(" BBOX = %i", FLAGS_GET_BBOX(point->flags) ? 1 : 0 ); lwnotice(" SRID = %i", (int)point->srid); printPA(point->point); lwnotice("}"); } /* @brief Clone LWPOINT object. Serialized point lists are not copied. * * @see ptarray_clone */ LWPOINT * lwpoint_clone(const LWPOINT *g) { LWPOINT *ret = lwalloc(sizeof(LWPOINT)); LWDEBUG(2, "lwpoint_clone called"); memcpy(ret, g, sizeof(LWPOINT)); ret->point = ptarray_clone(g->point); if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); return ret; } void lwpoint_release(LWPOINT *lwpoint) { lwgeom_release(lwpoint_as_lwgeom(lwpoint)); } /* check coordinate equality */ char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2) { return ptarray_same(p1->point, p2->point); } LWPOINT* lwpoint_force_dims(const LWPOINT *point, int hasz, int hasm) { POINTARRAY *pdims = NULL; LWPOINT *pointout; /* Return 2D empty */ if( lwpoint_is_empty(point) ) { pointout = lwpoint_construct_empty(point->srid, hasz, hasm); } else { /* Always we duplicate the ptarray and return */ pdims = ptarray_force_dims(point->point, hasz, hasm); pointout = lwpoint_construct(point->srid, NULL, pdims); } pointout->type = point->type; return pointout; } lwgeom/src/liblwgeom/lwout_twkb.c0000644000176200001440000004513213773172540016677 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #include "lwout_twkb.h" /* * GeometryType, and dimensions */ static uint8_t lwgeom_twkb_type(const LWGEOM *geom) { uint8_t twkb_type = 0; LWDEBUGF(2, "Entered lwgeom_twkb_type",0); switch ( geom->type ) { case POINTTYPE: twkb_type = WKB_POINT_TYPE; break; case LINETYPE: twkb_type = WKB_LINESTRING_TYPE; break; case TRIANGLETYPE: case POLYGONTYPE: twkb_type = WKB_POLYGON_TYPE; break; case MULTIPOINTTYPE: twkb_type = WKB_MULTIPOINT_TYPE; break; case MULTILINETYPE: twkb_type = WKB_MULTILINESTRING_TYPE; break; case MULTIPOLYGONTYPE: twkb_type = WKB_MULTIPOLYGON_TYPE; break; case TINTYPE: case COLLECTIONTYPE: twkb_type = WKB_GEOMETRYCOLLECTION_TYPE; break; default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } return twkb_type; } /** * Calculates the size of the bbox in varints in the form: * xmin, xdelta, ymin, ydelta */ static size_t sizeof_bbox(TWKB_STATE *ts, int ndims) { int i; uint8_t buf[16]; size_t size = 0; LWDEBUGF(2, "Entered %s", __func__); for ( i = 0; i < ndims; i++ ) { size += varint_s64_encode_buf(ts->bbox_min[i], buf); size += varint_s64_encode_buf((ts->bbox_max[i] - ts->bbox_min[i]), buf); } return size; } /** * Writes the bbox in varints in the form: * xmin, xdelta, ymin, ydelta */ static void write_bbox(TWKB_STATE *ts, int ndims) { int i; LWDEBUGF(2, "Entered %s", __func__); for ( i = 0; i < ndims; i++ ) { bytebuffer_append_varint(ts->header_buf, ts->bbox_min[i]); bytebuffer_append_varint(ts->header_buf, (ts->bbox_max[i] - ts->bbox_min[i])); } } /** * Stores a pointarray as varints in the buffer * @register_npoints, controls whether an npoints entry is added to the buffer (used to skip npoints for point types) * @dimension, states the dimensionality of object this array is part of (0 = point, 1 = linear, 2 = areal) */ static int ptarray_to_twkb_buf(const POINTARRAY *pa, TWKB_GLOBALS *globals, TWKB_STATE *ts, int register_npoints, uint32_t minpoints) { uint32_t ndims = FLAGS_NDIMS(pa->flags); uint32_t i, j; bytebuffer_t b; bytebuffer_t *b_p; int64_t nextdelta[MAX_N_DIMS]; int npoints = 0; size_t npoints_offset = 0; uint32_t max_points_left = pa->npoints; LWDEBUGF(2, "Entered %s", __func__); /* Dispense with the empty case right away */ if ( pa->npoints == 0 && register_npoints ) { LWDEBUGF(4, "Register npoints:%d", pa->npoints); bytebuffer_append_uvarint(ts->geom_buf, pa->npoints); return 0; } /* If npoints is more than 127 it is unpredictable how many bytes npoints will need */ /* Then we have to store the deltas in a temp buffer to later add them after npoints */ /* If noints is below 128 we know 1 byte will be needed */ /* Then we can make room for that 1 byte at once and write to */ /* ordinary buffer */ if( pa->npoints > 127 ) { /* Independent buffer to hold the coordinates, so we can put the npoints */ /* into the stream once we know how many points we actually have */ bytebuffer_init_with_size(&b, 3 * ndims * pa->npoints); b_p = &b; } else { /* We give an alias to our ordinary buffer */ b_p = ts->geom_buf; if ( register_npoints ) { /* We do not store a pointer to the place where we want the npoints value */ /* Instead we store how far from the beginning of the buffer we want the value */ /* That is because we otherwise will get in trouble if the buffer is reallocated */ npoints_offset = b_p->writecursor - b_p->buf_start; /* We just move the cursor 1 step to make room for npoints byte */ /* We use the function append_byte even if we have no value yet, */ /* since that gives us the check for big enough buffer and moves the cursor */ bytebuffer_append_byte(b_p, 0); } } for ( i = 0; i < pa->npoints; i++ ) { double *dbl_ptr = (double*)getPoint_internal(pa, i); int64_t diff = 0; /* Write this coordinate to the buffer as a varint */ for ( j = 0; j < ndims; j++ ) { /* To get the relative coordinate we don't get the distance */ /* from the last point but instead the distance from our */ /* last accumulated point. This is important to not build up an */ /* accumulated error when rounding the coordinates */ nextdelta[j] = (int64_t) llround(globals->factor[j] * dbl_ptr[j]) - ts->accum_rels[j]; LWDEBUGF(4, "deltavalue: %d, ", nextdelta[j]); diff += llabs(nextdelta[j]); } /* Skipping the first point is not allowed */ /* If the sum(abs()) of all the deltas was zero, */ /* then this was a duplicate point, so we can ignore it */ if ( i > 0 && diff == 0 && max_points_left > minpoints ) { max_points_left--; continue; } /* We really added a point, so... */ npoints++; /* Write this vertex to the temporary buffer as varints */ for ( j = 0; j < ndims; j++ ) { ts->accum_rels[j] += nextdelta[j]; bytebuffer_append_varint(b_p, nextdelta[j]); } /* See if this coordinate expands the bounding box */ if( globals->variant & TWKB_BBOX ) { for ( j = 0; j < ndims; j++ ) { if( ts->accum_rels[j] > ts->bbox_max[j] ) ts->bbox_max[j] = ts->accum_rels[j]; if( ts->accum_rels[j] < ts->bbox_min[j] ) ts->bbox_min[j] = ts->accum_rels[j]; } } } if ( pa->npoints > 127 ) { /* Now write the temporary results into the main buffer */ /* First the npoints */ if ( register_npoints ) bytebuffer_append_uvarint(ts->geom_buf, npoints); /* Now the coordinates */ bytebuffer_append_bytebuffer(ts->geom_buf, b_p); /* Clear our temporary buffer */ bytebuffer_destroy_buffer(&b); } else { /* If we didn't use a temp buffer, we just write that npoints value */ /* to where it belongs*/ if ( register_npoints ) varint_u64_encode_buf(npoints, b_p->buf_start + npoints_offset); } return 0; } /****************************************************************** * POINTS *******************************************************************/ static int lwpoint_to_twkb_buf(const LWPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts) { LWDEBUGF(2, "Entered %s", __func__); /* Set the coordinates (don't write npoints) */ ptarray_to_twkb_buf(pt->point, globals, ts, 0, 1); return 0; } /****************************************************************** * LINESTRINGS *******************************************************************/ static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts) { LWDEBUGF(2, "Entered %s", __func__); /* Set the coordinates (do write npoints) */ ptarray_to_twkb_buf(line->points, globals, ts, 1, 2); return 0; } static int lwtriangle_to_twkb_buf(const LWTRIANGLE *tri, TWKB_GLOBALS *globals, TWKB_STATE *ts) { LWDEBUGF(2, "Entered %s", __func__); bytebuffer_append_uvarint(ts->geom_buf, (uint64_t)1); /* Set the coordinates (do write npoints) */ ptarray_to_twkb_buf(tri->points, globals, ts, 1, 2); return 0; } /****************************************************************** * POLYGONS *******************************************************************/ static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts) { uint32_t i; /* Set the number of rings */ bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) poly->nrings); for ( i = 0; i < poly->nrings; i++ ) { /* Set the coordinates (do write npoints) */ ptarray_to_twkb_buf(poly->rings[i], globals, ts, 1, 4); } return 0; } /****************************************************************** * MULTI-GEOMETRYS (MultiPoint, MultiLinestring, MultiPolygon) *******************************************************************/ static int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { uint32_t i; int nempty = 0; LWDEBUGF(2, "Entered %s", __func__); LWDEBUGF(4, "Number of geometries in multi is %d", col->ngeoms); /* Deal with special case for MULTIPOINT: skip any empty points */ if ( col->type == MULTIPOINTTYPE ) { for ( i = 0; i < col->ngeoms; i++ ) if ( lwgeom_is_empty(col->geoms[i]) ) nempty++; } /* Set the number of geometries */ bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) (col->ngeoms - nempty)); /* We've been handed an idlist, so write it in */ if ( ts->idlist ) { for ( i = 0; i < col->ngeoms; i++ ) { /* Skip empty points in multipoints, we can't represent them */ if ( col->type == MULTIPOINTTYPE && lwgeom_is_empty(col->geoms[i]) ) continue; bytebuffer_append_varint(ts->geom_buf, ts->idlist[i]); } /* Empty it out to nobody else uses it now */ ts->idlist = NULL; } for ( i = 0; i < col->ngeoms; i++ ) { /* Skip empty points in multipoints, we can't represent them */ if ( col->type == MULTIPOINTTYPE && lwgeom_is_empty(col->geoms[i]) ) continue; lwgeom_to_twkb_buf(col->geoms[i], globals, ts); } return 0; } /****************************************************************** * GEOMETRYCOLLECTIONS *******************************************************************/ static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { uint32_t i; LWDEBUGF(2, "Entered %s", __func__); LWDEBUGF(4, "Number of geometries in collection is %d", col->ngeoms); /* Set the number of geometries */ bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) col->ngeoms); /* We've been handed an idlist, so write it in */ if ( ts->idlist ) { for ( i = 0; i < col->ngeoms; i++ ) bytebuffer_append_varint(ts->geom_buf, ts->idlist[i]); /* Empty it out to nobody else uses it now */ ts->idlist = NULL; } /* Write in the sub-geometries */ for ( i = 0; i < col->ngeoms; i++ ) { lwgeom_write_to_buffer(col->geoms[i], globals, ts); } return 0; } /****************************************************************** * Handle whole TWKB *******************************************************************/ static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts) { LWDEBUGF(2, "Entered %s", __func__); switch ( geom->type ) { case POINTTYPE: { LWDEBUGF(4,"Type found is Point, %d", geom->type); return lwpoint_to_twkb_buf((LWPOINT*) geom, globals, ts); } case LINETYPE: { LWDEBUGF(4,"Type found is Linestring, %d", geom->type); return lwline_to_twkb_buf((LWLINE*) geom, globals, ts); } case TRIANGLETYPE: { LWDEBUGF(4, "Type found is Triangle, %d", geom->type); return lwtriangle_to_twkb_buf((LWTRIANGLE *)geom, globals, ts); } /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: { LWDEBUGF(4,"Type found is Polygon, %d", geom->type); return lwpoly_to_twkb_buf((LWPOLY*)geom, globals, ts); } /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: { LWDEBUGF(4,"Type found is Multi, %d", geom->type); return lwmulti_to_twkb_buf((LWCOLLECTION*)geom, globals, ts); } case COLLECTIONTYPE: case TINTYPE: { LWDEBUGF(4,"Type found is collection, %d", geom->type); return lwcollection_to_twkb_buf((LWCOLLECTION*) geom, globals, ts); } /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } return 0; } static int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state) { int i, is_empty, has_z = 0, has_m = 0, ndims; size_t bbox_size = 0, optional_precision_byte = 0; uint8_t flag = 0, type_prec = 0; bytebuffer_t header_bytebuffer, geom_bytebuffer; TWKB_STATE child_state; memset(&child_state, 0, sizeof(TWKB_STATE)); child_state.header_buf = &header_bytebuffer; child_state.geom_buf = &geom_bytebuffer; child_state.idlist = parent_state->idlist; bytebuffer_init_with_size(child_state.header_buf, 16); bytebuffer_init_with_size(child_state.geom_buf, 64); /* Read dimensionality from input */ ndims = lwgeom_ndims(geom); is_empty = lwgeom_is_empty(geom); if ( ndims > 2 ) { has_z = lwgeom_has_z(geom); has_m = lwgeom_has_m(geom); } /* Do we need extended precision? If we have a Z or M we do. */ optional_precision_byte = (has_z || has_m); /* Both X and Y dimension use the same precision */ globals->factor[0] = pow(10, globals->prec_xy); globals->factor[1] = globals->factor[0]; /* Z and M dimensions have their own precisions */ if ( has_z ) globals->factor[2] = pow(10, globals->prec_z); if ( has_m ) globals->factor[2 + has_z] = pow(10, globals->prec_m); /* Reset stats */ for ( i = 0; i < MAX_N_DIMS; i++ ) { /* Reset bbox calculation */ child_state.bbox_max[i] = INT64_MIN; child_state.bbox_min[i] = INT64_MAX; /* Reset acumulated delta values to get absolute values on next point */ child_state.accum_rels[i] = 0; } /* TYPE/PRECISION BYTE */ if ( abs(globals->prec_xy) > 7 ) lwerror("%s: X/Z precision cannot be greater than 7 or less than -7", __func__); /* Read the TWKB type number from the geometry */ TYPE_PREC_SET_TYPE(type_prec, lwgeom_twkb_type(geom)); /* Zig-zag the precision value before encoding it since it is a signed value */ TYPE_PREC_SET_PREC(type_prec, zigzag8(globals->prec_xy)); /* Write the type and precision byte */ bytebuffer_append_byte(child_state.header_buf, type_prec); /* METADATA BYTE */ /* Set first bit if we are going to store bboxes */ FIRST_BYTE_SET_BBOXES(flag, (globals->variant & TWKB_BBOX) && ! is_empty); /* Set second bit if we are going to store resulting size */ FIRST_BYTE_SET_SIZES(flag, globals->variant & TWKB_SIZE); /* There will be no ID-list (for now) */ FIRST_BYTE_SET_IDLIST(flag, parent_state->idlist && ! is_empty); /* Are there higher dimensions */ FIRST_BYTE_SET_EXTENDED(flag, optional_precision_byte); /* Empty? */ FIRST_BYTE_SET_EMPTY(flag, is_empty); /* Write the header byte */ bytebuffer_append_byte(child_state.header_buf, flag); /* EXTENDED PRECISION BYTE (OPTIONAL) */ /* If needed, write the extended dim byte */ if( optional_precision_byte ) { uint8_t flag = 0; if ( has_z && ( globals->prec_z > 7 || globals->prec_z < 0 ) ) lwerror("%s: Z precision cannot be negative or greater than 7", __func__); if ( has_m && ( globals->prec_m > 7 || globals->prec_m < 0 ) ) lwerror("%s: M precision cannot be negative or greater than 7", __func__); HIGHER_DIM_SET_HASZ(flag, has_z); HIGHER_DIM_SET_HASM(flag, has_m); HIGHER_DIM_SET_PRECZ(flag, globals->prec_z); HIGHER_DIM_SET_PRECM(flag, globals->prec_m); bytebuffer_append_byte(child_state.header_buf, flag); } /* It the geometry is empty, we're almost done */ if ( is_empty ) { /* If this output is sized, write the size of */ /* all following content, which is zero because */ /* there is none */ if ( globals->variant & TWKB_SIZE ) bytebuffer_append_byte(child_state.header_buf, 0); bytebuffer_append_bytebuffer(parent_state->geom_buf, child_state.header_buf); bytebuffer_destroy_buffer(child_state.header_buf); bytebuffer_destroy_buffer(child_state.geom_buf); return 0; } /* Write the TWKB into the output buffer */ lwgeom_to_twkb_buf(geom, globals, &child_state); /*If we have a header_buf, we know that this function is called inside a collection*/ /*and then we have to merge the bboxes of the included geometries*/ /*and put the result to the parent (the collection)*/ if( (globals->variant & TWKB_BBOX) && parent_state->header_buf ) { LWDEBUG(4,"Merge bboxes"); for ( i = 0; i < ndims; i++ ) { if(child_state.bbox_min[i]bbox_min[i]) parent_state->bbox_min[i] = child_state.bbox_min[i]; if(child_state.bbox_max[i]>parent_state->bbox_max[i]) parent_state->bbox_max[i] = child_state.bbox_max[i]; } } /* Did we have a box? If so, how big? */ bbox_size = 0; if( globals->variant & TWKB_BBOX ) { LWDEBUG(4,"We want boxes and will calculate required size"); bbox_size = sizeof_bbox(&child_state, ndims); } /* Write the size if wanted */ if( globals->variant & TWKB_SIZE ) { /* Here we have to add what we know will be written to header */ /* buffer after size value is written */ size_t size_to_register = bytebuffer_getlength(child_state.geom_buf); size_to_register += bbox_size; bytebuffer_append_uvarint(child_state.header_buf, size_to_register); } if( globals->variant & TWKB_BBOX ) write_bbox(&child_state, ndims); bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.header_buf); bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.geom_buf); bytebuffer_destroy_buffer(child_state.header_buf); bytebuffer_destroy_buffer(child_state.geom_buf); return 0; } /** * Convert LWGEOM to a char* in TWKB format. Caller is responsible for freeing * the returned array. */ uint8_t* lwgeom_to_twkb_with_idlist(const LWGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size) { LWDEBUGF(2, "Entered %s", __func__); LWDEBUGF(2, "variant value %x", variant); TWKB_GLOBALS tg; TWKB_STATE ts; bytebuffer_t geom_bytebuffer; uint8_t *twkb; memset(&ts, 0, sizeof(TWKB_STATE)); memset(&tg, 0, sizeof(TWKB_GLOBALS)); tg.variant = variant; tg.prec_xy = precision_xy; tg.prec_z = precision_z; tg.prec_m = precision_m; if ( idlist && ! lwgeom_is_collection(geom) ) { lwerror("Only collections can support ID lists"); return NULL; } if ( ! geom ) { LWDEBUG(4,"Cannot convert NULL into TWKB."); lwerror("Cannot convert NULL into TWKB"); return NULL; } ts.idlist = idlist; ts.header_buf = NULL; ts.geom_buf = &geom_bytebuffer; bytebuffer_init_with_size(ts.geom_buf, 512); lwgeom_write_to_buffer(geom, &tg, &ts); twkb = bytebuffer_get_buffer_copy(ts.geom_buf, twkb_size); bytebuffer_destroy_buffer(ts.geom_buf); return twkb; } uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size) { return lwgeom_to_twkb_with_idlist(geom, NULL, variant, precision_xy, precision_z, precision_m, twkb_size); } lwgeom/src/liblwgeom/lwspheroid.c0000644000176200001440000005242413773172540016660 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * Copyright (C) 2009 David Skea * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeodetic.h" #include "lwgeom_log.h" /* In proj4.9, GeographicLib is in special header */ #ifdef PROJ_GEODESIC #include #endif /** * Initialize spheroid object based on major and minor axis */ void spheroid_init(SPHEROID *s, double a, double b) { s->a = a; s->b = b; s->f = (a - b) / a; s->e_sq = (a*a - b*b)/(a*a); s->radius = (2.0 * a + b ) / 3.0; } #ifndef PROJ_GEODESIC static double spheroid_mu2(double alpha, const SPHEROID *s) { double b2 = POW2(s->b); return POW2(cos(alpha)) * (POW2(s->a) - b2) / b2; } static double spheroid_big_a(double u2) { return 1.0 + (u2 / 16384.0) * (4096.0 + u2 * (-768.0 + u2 * (320.0 - 175.0 * u2))); } static double spheroid_big_b(double u2) { return (u2 / 1024.0) * (256.0 + u2 * (-128.0 + u2 * (74.0 - 47.0 * u2))); } #endif /* ! PROJ_GEODESIC */ #ifdef PROJ_GEODESIC /** * Computes the shortest distance along the surface of the spheroid * between two points, using the inverse geodesic problem from * GeographicLib (Karney 2013). * * @param a - location of first point * @param b - location of second point * @param s - spheroid to calculate on * @return spheroidal distance between a and b in spheroid units */ double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = a->lat * 180.0 / M_PI; double lon1 = a->lon * 180.0 / M_PI; double lat2 = b->lat * 180.0 / M_PI; double lon2 = b->lon * 180.0 / M_PI; double s12 = 0.0; /* return distance */ geod_inverse(&gd, lat1, lon1, lat2, lon2, &s12, 0, 0); return s12; } /** * Computes the forward azimuth of the geodesic joining two points on * the spheroid, using the inverse geodesic problem (Karney 2013). * * @param r - location of first point * @param s - location of second point * @return azimuth of line joining r to s (but not reverse) */ double spheroid_direction(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = a->lat * 180.0 / M_PI; double lon1 = a->lon * 180.0 / M_PI; double lat2 = b->lat * 180.0 / M_PI; double lon2 = b->lon * 180.0 / M_PI; double azi1; /* return azimuth */ geod_inverse(&gd, lat1, lon1, lat2, lon2, 0, &azi1, 0); return azi1 * M_PI / 180.0; } /** * Given a location, an azimuth and a distance, computes the location of * the projected point. Using the direct geodesic problem from * GeographicLib (Karney 2013). * * @param r - location of first point * @param distance - distance in meters * @param azimuth - azimuth in radians * @return g - location of projected point */ int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = r->lat * 180.0 / M_PI; double lon1 = r->lon * 180.0 / M_PI; double lat2, lon2; /* return projected position */ geod_direct(&gd, lat1, lon1, azimuth * 180.0 / M_PI, distance, &lat2, &lon2, 0); g->lat = lat2 * M_PI / 180.0; g->lon = lon2 * M_PI / 180.0; return LW_SUCCESS; } static double ptarray_area_spheroid(const POINTARRAY *pa, const SPHEROID *spheroid) { /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); struct geod_polygon poly; geod_polygon_init(&poly, 0); uint32_t i; double area; /* returned polygon area */ POINT2D p; /* long/lat units are degrees */ /* Pass points from point array; don't close the linearring */ for ( i = 0; i < pa->npoints - 1; i++ ) { getPoint2d_p(pa, i, &p); geod_polygon_addpoint(&gd, &poly, p.y, p.x); LWDEBUGF(4, "geod_polygon_addpoint %d: %.12g %.12g", i, p.y, p.x); } i = geod_polygon_compute(&gd, &poly, 0, 1, &area, 0); if ( i != pa->npoints - 1 ) { lwerror("ptarray_area_spheroid: different number of points %d vs %d", i, pa->npoints - 1); } LWDEBUGF(4, "geod_polygon_compute area: %.12g", area); return fabs(area); } /* Above use Proj GeographicLib */ #else /* ! PROJ_GEODESIC */ /* Below use pre-version 2.2 geodesic functions */ /** * Computes the shortest distance along the surface of the spheroid * between two points. Based on Vincenty's formula for the geodetic * inverse problem as described in "Geocentric Datum of Australia * Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1a.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_inverse.jsp * * @param a - location of first point. * @param b - location of second point. * @param s - spheroid to calculate on * @return spheroidal distance between a and b in spheroid units. */ double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { double lambda = (b->lon - a->lon); double f = spheroid->f; double omf = 1 - spheroid->f; double u1, u2; double cos_u1, cos_u2; double sin_u1, sin_u2; double big_a, big_b, delta_sigma; double alpha, sin_alpha, cos_alphasq, c; double sigma, sin_sigma, cos_sigma, cos2_sigma_m, sqrsin_sigma, last_lambda, omega; double cos_lambda, sin_lambda; double distance; int i = 0; /* Same point => zero distance */ if ( geographic_point_equals(a, b) ) { return 0.0; } u1 = atan(omf * tan(a->lat)); cos_u1 = cos(u1); sin_u1 = sin(u1); u2 = atan(omf * tan(b->lat)); cos_u2 = cos(u2); sin_u2 = sin(u2); omega = lambda; do { cos_lambda = cos(lambda); sin_lambda = sin(lambda); sqrsin_sigma = POW2(cos_u2 * sin_lambda) + POW2((cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda)); sin_sigma = sqrt(sqrsin_sigma); cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda; sigma = atan2(sin_sigma, cos_sigma); sin_alpha = cos_u1 * cos_u2 * sin_lambda / sin(sigma); /* Numerical stability issue, ensure asin is not NaN */ if ( sin_alpha > 1.0 ) alpha = M_PI_2; else if ( sin_alpha < -1.0 ) alpha = -1.0 * M_PI_2; else alpha = asin(sin_alpha); cos_alphasq = POW2(cos(alpha)); cos2_sigma_m = cos(sigma) - (2.0 * sin_u1 * sin_u2 / cos_alphasq); /* Numerical stability issue, cos2 is in range */ if ( cos2_sigma_m > 1.0 ) cos2_sigma_m = 1.0; if ( cos2_sigma_m < -1.0 ) cos2_sigma_m = -1.0; c = (f / 16.0) * cos_alphasq * (4.0 + f * (4.0 - 3.0 * cos_alphasq)); last_lambda = lambda; lambda = omega + (1.0 - c) * f * sin(alpha) * (sigma + c * sin(sigma) * (cos2_sigma_m + c * cos(sigma) * (-1.0 + 2.0 * POW2(cos2_sigma_m)))); i++; } while ( (i < 999) && (lambda != 0.0) && (fabs((last_lambda - lambda)/lambda) > 1.0e-9) ); u2 = spheroid_mu2(alpha, spheroid); big_a = spheroid_big_a(u2); big_b = spheroid_big_b(u2); delta_sigma = big_b * sin_sigma * (cos2_sigma_m + (big_b / 4.0) * (cos_sigma * (-1.0 + 2.0 * POW2(cos2_sigma_m)) - (big_b / 6.0) * cos2_sigma_m * (-3.0 + 4.0 * sqrsin_sigma) * (-3.0 + 4.0 * POW2(cos2_sigma_m)))); distance = spheroid->b * big_a * (sigma - delta_sigma); /* Algorithm failure, distance == NaN, fallback to sphere */ if ( distance != distance ) { lwerror("spheroid_distance returned NaN: (%.20g %.20g) (%.20g %.20g) a = %.20g b = %.20g",a->lat, a->lon, b->lat, b->lon, spheroid->a, spheroid->b); return spheroid->radius * sphere_distance(a, b); } return distance; } /** * Computes the direction of the geodesic joining two points on * the spheroid. Based on Vincenty's formula for the geodetic * inverse problem as described in "Geocentric Datum of Australia * Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1a.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_inverse.jsp * * @param r - location of first point * @param s - location of second point * @return azimuth of line joining r and s */ double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid) { int i = 0; double lambda = s->lon - r->lon; double omf = 1 - spheroid->f; double u1 = atan(omf * tan(r->lat)); double cos_u1 = cos(u1); double sin_u1 = sin(u1); double u2 = atan(omf * tan(s->lat)); double cos_u2 = cos(u2); double sin_u2 = sin(u2); double omega = lambda; double alpha, sigma, sin_sigma, cos_sigma, cos2_sigma_m, sqr_sin_sigma, last_lambda; double sin_alpha, cos_alphasq, C, alphaFD; do { sqr_sin_sigma = POW2(cos_u2 * sin(lambda)) + POW2((cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos(lambda))); sin_sigma = sqrt(sqr_sin_sigma); cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos(lambda); sigma = atan2(sin_sigma, cos_sigma); sin_alpha = cos_u1 * cos_u2 * sin(lambda) / sin(sigma); /* Numerical stability issue, ensure asin is not NaN */ if ( sin_alpha > 1.0 ) alpha = M_PI_2; else if ( sin_alpha < -1.0 ) alpha = -1.0 * M_PI_2; else alpha = asin(sin_alpha); cos_alphasq = POW2(cos(alpha)); cos2_sigma_m = cos(sigma) - (2.0 * sin_u1 * sin_u2 / cos_alphasq); /* Numerical stability issue, cos2 is in range */ if ( cos2_sigma_m > 1.0 ) cos2_sigma_m = 1.0; if ( cos2_sigma_m < -1.0 ) cos2_sigma_m = -1.0; C = (spheroid->f / 16.0) * cos_alphasq * (4.0 + spheroid->f * (4.0 - 3.0 * cos_alphasq)); last_lambda = lambda; lambda = omega + (1.0 - C) * spheroid->f * sin(alpha) * (sigma + C * sin(sigma) * (cos2_sigma_m + C * cos(sigma) * (-1.0 + 2.0 * POW2(cos2_sigma_m)))); i++; } while ( (i < 999) && (lambda != 0) && (fabs((last_lambda - lambda) / lambda) > 1.0e-9) ); alphaFD = atan2((cos_u2 * sin(lambda)), (cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos(lambda))); if (alphaFD < 0.0) { alphaFD = alphaFD + 2.0 * M_PI; } if (alphaFD > 2.0 * M_PI) { alphaFD = alphaFD - 2.0 * M_PI; } return alphaFD; } /** * Given a location, an azimuth and a distance, computes the * location of the projected point. Based on Vincenty's formula * for the geodetic direct problem as described in "Geocentric * Datum of Australia Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1b.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_direct.jsp * * @param r - location of first point. * @param distance - distance in meters. * @param azimuth - azimuth in radians. * @return s - location of projected point. */ int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g) { double omf = 1 - spheroid->f; double tan_u1 = omf * tan(r->lat); double u1 = atan(tan_u1); double sigma, last_sigma, delta_sigma, two_sigma_m; double sigma1, sin_alpha, alpha, cos_alphasq; double u2, A, B; double lat2, lambda, lambda2, C, omega; int i = 0; if (azimuth < 0.0) { azimuth = azimuth + M_PI * 2.0; } if (azimuth > (M_PI * 2.0)) { azimuth = azimuth - M_PI * 2.0; } sigma1 = atan2(tan_u1, cos(azimuth)); sin_alpha = cos(u1) * sin(azimuth); alpha = asin(sin_alpha); cos_alphasq = 1.0 - POW2(sin_alpha); u2 = spheroid_mu2(alpha, spheroid); A = spheroid_big_a(u2); B = spheroid_big_b(u2); sigma = (distance / (spheroid->b * A)); do { two_sigma_m = 2.0 * sigma1 + sigma; delta_sigma = B * sin(sigma) * (cos(two_sigma_m) + (B / 4.0) * (cos(sigma) * (-1.0 + 2.0 * POW2(cos(two_sigma_m)) - (B / 6.0) * cos(two_sigma_m) * (-3.0 + 4.0 * POW2(sin(sigma))) * (-3.0 + 4.0 * POW2(cos(two_sigma_m)))))); last_sigma = sigma; sigma = (distance / (spheroid->b * A)) + delta_sigma; i++; } while (i < 999 && fabs((last_sigma - sigma) / sigma) > 1.0e-9); lat2 = atan2((sin(u1) * cos(sigma) + cos(u1) * sin(sigma) * cos(azimuth)), (omf * sqrt(POW2(sin_alpha) + POW2(sin(u1) * sin(sigma) - cos(u1) * cos(sigma) * cos(azimuth))))); lambda = atan2((sin(sigma) * sin(azimuth)), (cos(u1) * cos(sigma) - sin(u1) * sin(sigma) * cos(azimuth))); C = (spheroid->f / 16.0) * cos_alphasq * (4.0 + spheroid->f * (4.0 - 3.0 * cos_alphasq)); omega = lambda - (1.0 - C) * spheroid->f * sin_alpha * (sigma + C * sin(sigma) * (cos(two_sigma_m) + C * cos(sigma) * (-1.0 + 2.0 * POW2(cos(two_sigma_m))))); lambda2 = r->lon + omega; g->lat = lat2; g->lon = lambda2; return LW_SUCCESS; } static inline double spheroid_prime_vertical_radius_of_curvature(double latitude, const SPHEROID *spheroid) { return spheroid->a / (sqrt(1.0 - spheroid->e_sq * POW2(sin(latitude)))); } static inline double spheroid_parallel_arc_length(double latitude, double deltaLongitude, const SPHEROID *spheroid) { return spheroid_prime_vertical_radius_of_curvature(latitude, spheroid) * cos(latitude) * deltaLongitude; } /** * Computes the area on the spheroid of a box bounded by meridians and * parallels. The box is defined by two points, the South West corner * and the North East corner. Formula based on Bagratuni 1967. * * @param southWestCorner - lower left corner of bounding box. * @param northEastCorner - upper right corner of bounding box. * @return area in square meters. */ static double spheroid_boundingbox_area(const GEOGRAPHIC_POINT *southWestCorner, const GEOGRAPHIC_POINT *northEastCorner, const SPHEROID *spheroid) { double z0 = (northEastCorner->lon - southWestCorner->lon) * POW2(spheroid->b) / 2.0; double e = sqrt(spheroid->e_sq); double sinPhi1 = sin(southWestCorner->lat); double sinPhi2 = sin(northEastCorner->lat); double t1p1 = sinPhi1 / (1.0 - spheroid->e_sq * sinPhi1 * sinPhi1); double t1p2 = sinPhi2 / (1.0 - spheroid->e_sq * sinPhi2 * sinPhi2); double oneOver2e = 1.0 / (2.0 * e); double t2p1 = oneOver2e * log((1.0 + e * sinPhi1) / (1.0 - e * sinPhi1)); double t2p2 = oneOver2e * log((1.0 + e * sinPhi2) / (1.0 - e * sinPhi2)); return z0 * (t1p2 + t2p2) - z0 * (t1p1 + t2p1); } /** * This function doesn't work for edges crossing the dateline or in the southern * hemisphere. Points are pre-conditioned in ptarray_area_spheroid. */ static double spheroid_striparea(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, double latitude_min, const SPHEROID *spheroid) { GEOGRAPHIC_POINT A, B, mL, nR; double deltaLng, baseArea, topArea; double bE, tE, ratio, sign; A = *a; B = *b; mL.lat = latitude_min; mL.lon = FP_MIN(A.lon, B.lon); nR.lat = FP_MIN(A.lat, B.lat); nR.lon = FP_MAX(A.lon, B.lon); LWDEBUGF(4, "mL (%.12g %.12g)", mL.lat, mL.lon); LWDEBUGF(4, "nR (%.12g %.12g)", nR.lat, nR.lon); baseArea = spheroid_boundingbox_area(&mL, &nR, spheroid); LWDEBUGF(4, "baseArea %.12g", baseArea); mL.lat = FP_MIN(A.lat, B.lat); mL.lon = FP_MIN(A.lon, B.lon); nR.lat = FP_MAX(A.lat, B.lat); nR.lon = FP_MAX(A.lon, B.lon); LWDEBUGF(4, "mL (%.12g %.12g)", mL.lat, mL.lon); LWDEBUGF(4, "nR (%.12g %.12g)", nR.lat, nR.lon); topArea = spheroid_boundingbox_area(&mL, &nR, spheroid); LWDEBUGF(4, "topArea %.12g", topArea); deltaLng = B.lon - A.lon; LWDEBUGF(4, "deltaLng %.12g", deltaLng); bE = spheroid_parallel_arc_length(A.lat, deltaLng, spheroid); tE = spheroid_parallel_arc_length(B.lat, deltaLng, spheroid); LWDEBUGF(4, "bE %.12g", bE); LWDEBUGF(4, "tE %.12g", tE); ratio = (bE + tE)/tE; sign = SIGNUM(B.lon - A.lon); return (baseArea + topArea / ratio) * sign; } static double ptarray_area_spheroid(const POINTARRAY *pa, const SPHEROID *spheroid) { GEOGRAPHIC_POINT a, b; POINT2D p; uint32_t i; double area = 0.0; GBOX gbox2d; int in_south = LW_FALSE; double delta_lon_tolerance; double latitude_min; gbox2d.flags = lwflags(0, 0, 0); /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; /* Get the raw min/max values for the latitudes */ ptarray_calculate_gbox_cartesian(pa, &gbox2d); if ( SIGNUM(gbox2d.ymin) != SIGNUM(gbox2d.ymax) ) lwerror("ptarray_area_spheroid: cannot handle ptarray that crosses equator"); /* Geodetic bbox < 0.0 implies geometry is entirely in southern hemisphere */ if ( gbox2d.ymax < 0.0 ) in_south = LW_TRUE; LWDEBUGF(4, "gbox2d.ymax %.12g", gbox2d.ymax); /* Tolerance for strip area calculation */ if ( in_south ) { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymin) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(fabs(gbox2d.ymax)); } else { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymax) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(gbox2d.ymin); } /* Initialize first point */ getPoint2d_p(pa, 0, &p); geographic_point_init(p.x, p.y, &a); for ( i = 1; i < pa->npoints; i++ ) { GEOGRAPHIC_POINT a1, b1; double strip_area = 0.0; double delta_lon = 0.0; LWDEBUGF(4, "edge #%d", i); getPoint2d_p(pa, i, &p); geographic_point_init(p.x, p.y, &b); a1 = a; b1 = b; /* Flip into north if in south */ if ( in_south ) { a1.lat = -1.0 * a1.lat; b1.lat = -1.0 * b1.lat; } LWDEBUGF(4, "in_south %d", in_south); LWDEBUGF(4, "crosses_dateline(a, b) %d", crosses_dateline(&a, &b) ); if ( crosses_dateline(&a, &b) ) { double shift; if ( a1.lon > 0.0 ) shift = (M_PI - a1.lon) + 0.088; /* About 5deg more */ else shift = (M_PI - b1.lon) + 0.088; /* About 5deg more */ LWDEBUGF(4, "shift: %.8g", shift); LWDEBUGF(4, "before shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); point_shift(&a1, shift); point_shift(&b1, shift); LWDEBUGF(4, "after shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); } delta_lon = fabs(b1.lon - a1.lon); LWDEBUGF(4, "a1(%.18g %.18g) b1(%.18g %.18g)", a1.lat, a1.lon, b1.lat, b1.lon); LWDEBUGF(4, "delta_lon %.18g", delta_lon); LWDEBUGF(4, "delta_lon_tolerance %.18g", delta_lon_tolerance); if ( delta_lon > 0.0 ) { if ( delta_lon < delta_lon_tolerance ) { strip_area = spheroid_striparea(&a1, &b1, latitude_min, spheroid); LWDEBUGF(4, "strip_area %.12g", strip_area); area += strip_area; } else { GEOGRAPHIC_POINT p, q; double step = floor(delta_lon / delta_lon_tolerance); double distance = spheroid_distance(&a1, &b1, spheroid); double pDistance = 0.0; int j = 0; LWDEBUGF(4, "step %.18g", step); LWDEBUGF(4, "distance %.18g", distance); step = distance / step; LWDEBUGF(4, "step %.18g", step); p = a1; while (pDistance < (distance - step * 1.01)) { double azimuth = spheroid_direction(&p, &b1, spheroid); j++; LWDEBUGF(4, " iteration %d", j); LWDEBUGF(4, " azimuth %.12g", azimuth); pDistance = pDistance + step; LWDEBUGF(4, " pDistance %.12g", pDistance); spheroid_project(&p, spheroid, step, azimuth, &q); strip_area = spheroid_striparea(&p, &q, latitude_min, spheroid); LWDEBUGF(4, " strip_area %.12g", strip_area); area += strip_area; LWDEBUGF(4, " area %.12g", area); p.lat = q.lat; p.lon = q.lon; } strip_area = spheroid_striparea(&p, &b1, latitude_min, spheroid); area += strip_area; } } /* B gets incremented in the next loop, so we save the value here */ a = b; } return fabs(area); } #endif /* else ! PROJ_GEODESIC */ /** * Calculate the area of an LWGEOM. Anything except POLYGON, MULTIPOLYGON * and GEOMETRYCOLLECTION return zero immediately. Multi's recurse, polygons * calculate external ring area and subtract internal ring area. A GBOX is * required to check relationship to equator an outside point. * WARNING: Does NOT WORK for polygons over equator or pole. */ double lwgeom_area_spheroid(const LWGEOM *lwgeom, const SPHEROID *spheroid) { int type; assert(lwgeom); /* No area in nothing */ if ( lwgeom_is_empty(lwgeom) ) return 0.0; /* Read the geometry type number */ type = lwgeom->type; /* Anything but polygons and collections returns zero */ if ( ! ( type == POLYGONTYPE || type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ) ) return 0.0; /* Actually calculate area */ if ( type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)lwgeom; uint32_t i; double area = 0.0; /* Just in case there's no rings */ if ( poly->nrings < 1 ) return 0.0; /* First, the area of the outer ring */ area += ptarray_area_spheroid(poly->rings[0], spheroid); /* Subtract areas of inner rings */ for ( i = 1; i < poly->nrings; i++ ) { area -= ptarray_area_spheroid(poly->rings[i], spheroid); } return area; } /* Recurse into sub-geometries to get area */ if ( type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ) { LWCOLLECTION *col = (LWCOLLECTION*)lwgeom; uint32_t i; double area = 0.0; for ( i = 0; i < col->ngeoms; i++ ) { area += lwgeom_area_spheroid(col->geoms[i], spheroid); } return area; } /* Shouldn't get here. */ return 0.0; } lwgeom/src/liblwgeom/lwalgorithm.c0000644000176200001440000005104113773172540017023 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2008 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include /* for tolower */ #include int p4d_same(const POINT4D *p1, const POINT4D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) && FP_EQUALS(p1->m,p2->m) ) return LW_TRUE; else return LW_FALSE; } int p3d_same(const POINT3D *p1, const POINT3D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) ) return LW_TRUE; else return LW_FALSE; } int p2d_same(const POINT2D *p1, const POINT2D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) ) return LW_TRUE; else return LW_FALSE; } /** * lw_segment_side() * * Return -1 if point Q is left of segment P * Return 1 if point Q is right of segment P * Return 0 if point Q in on segment P */ int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q) { double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) ); return SIGNUM(side); } /** * Returns the length of a linear segment */ double lw_seg_length(const POINT2D *A1, const POINT2D *A2) { return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y)); } /** * Returns true if P is on the same side of the plane partition * defined by A1/A3 as A2 is. Only makes sense if P has already been * determined to be on the circle defined by A1/A2/A3. */ int lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) { return lw_segment_side(A1, A3, A2) == lw_segment_side(A1, A3, P); } /** * Returns true if P is between A1/A2. Only makes sense if P has already been * deterined to be on the line defined by A1/A2. */ int lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2) { return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) || ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y)); } /** * Returns true if arc A is actually a point (all vertices are the same) . */ int lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) { if ( A1->x == A2->x && A2->x == A3->x && A1->y == A2->y && A2->y == A3->y ) return LW_TRUE; else return LW_FALSE; } /** * Returns the length of a circular arc segment */ double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) { POINT2D C; double radius_A, circumference_A; int a2_side, clockwise; double a1, a3; double angle; if ( lw_arc_is_pt(A1, A2, A3) ) return 0.0; radius_A = lw_arc_center(A1, A2, A3, &C); /* Co-linear! Return linear distance! */ if ( radius_A < 0 ) { double dx = A1->x - A3->x; double dy = A1->y - A3->y; return sqrt(dx*dx + dy*dy); } /* Closed circle! Return the circumference! */ circumference_A = M_PI * 2 * radius_A; if ( p2d_same(A1, A3) ) return circumference_A; /* Determine the orientation of the arc */ a2_side = lw_segment_side(A1, A3, A2); /* The side of the A1/A3 line that A2 falls on dictates the sweep direction from A1 to A3. */ if ( a2_side == -1 ) clockwise = LW_TRUE; else clockwise = LW_FALSE; /* Angles of each point that defines the arc section */ a1 = atan2(A1->y - C.y, A1->x - C.x); a3 = atan2(A3->y - C.y, A3->x - C.x); /* What's the sweep from A1 to A3? */ if ( clockwise ) { if ( a1 > a3 ) angle = a1 - a3; else angle = 2*M_PI + a1 - a3; } else { if ( a3 > a1 ) angle = a3 - a1; else angle = 2*M_PI + a3 - a1; } /* Length as proportion of circumference */ return circumference_A * (angle / (2*M_PI)); } int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q) { POINT2D C; double radius_A; double side_Q, side_A2; double d; side_Q = lw_segment_side(A1, A3, Q); radius_A = lw_arc_center(A1, A2, A3, &C); side_A2 = lw_segment_side(A1, A3, A2); /* Linear case */ if ( radius_A < 0 ) return side_Q; d = distance2d_pt_pt(Q, &C); /* Q is on the arc boundary */ if ( d == radius_A && side_Q == side_A2 ) { return 0; } /* Q on A1-A3 line, so its on opposite side to A2 */ if ( side_Q == 0 ) { return -1 * side_A2; } /* * Q is inside the arc boundary, so it's not on the side we * might think from examining only the end points */ if ( d < radius_A && side_Q == side_A2 ) { side_Q *= -1; } return side_Q; } /** * Determines the center of the circle defined by the three given points. * In the event the circle is complete, the midpoint of the segment defined * by the first and second points is returned. If the points are collinear, * as determined by equal slopes, then -1.0 is returned. If the interior * point is coincident with either end point, they are taken as collinear. * For non-collinear cases, arc radious is returned. */ double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result) { POINT2D c; double cx, cy, cr; double dx21, dy21, dx31, dy31, h21, h31, d; c.x = c.y = 0.0; LWDEBUGF(2, "lw_arc_center called (%.16f,%.16f), (%.16f,%.16f), (%.16f,%.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y); /* Closed circle */ if (fabs(p1->x - p3->x) < EPSILON_SQLMM && fabs(p1->y - p3->y) < EPSILON_SQLMM) { cx = p1->x + (p2->x - p1->x) / 2.0; cy = p1->y + (p2->y - p1->y) / 2.0; c.x = cx; c.y = cy; *result = c; cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0)); return cr; } /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */ dx21 = p2->x - p1->x; dy21 = p2->y - p1->y; dx31 = p3->x - p1->x; dy31 = p3->y - p1->y; h21 = pow(dx21, 2.0) + pow(dy21, 2.0); h31 = pow(dx31, 2.0) + pow(dy31, 2.0); /* 2 * |Cross product|, d<0 means clockwise and d>0 counterclockwise sweeping angle */ d = 2 * (dx21 * dy31 - dx31 * dy21); /* Check colinearity, |Cross product| = 0 */ if (fabs(d) < EPSILON_SQLMM) return -1.0; /* Calculate centroid coordinates and radius */ cx = p1->x + (h21 * dy31 - h31 * dy21) / d; cy = p1->y - (h21 * dx31 - h31 * dx21) / d; c.x = cx; c.y = cy; *result = c; cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2)); LWDEBUGF(2, "lw_arc_center center is (%.16f,%.16f)", result->x, result->y); return cr; } int pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring) { int cn = 0; /* the crossing number counter */ uint32_t i; const POINT2D *v1, *v2; const POINT2D *first, *last; first = getPoint2d_cp(ring, 0); last = getPoint2d_cp(ring, ring->npoints-1); if ( memcmp(first, last, sizeof(POINT2D)) ) { lwerror("pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)", first->x, first->y, last->x, last->y); return LW_FALSE; } LWDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y); /* printPA(ring); */ /* loop through all edges of the polygon */ v1 = getPoint2d_cp(ring, 0); for (i=0; inpoints-1; i++) { double vt; v2 = getPoint2d_cp(ring, i+1); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1->y <= p->y) && (v2->y > p->y)) /* a downward crossing */ || ((v1->y > p->y) && (v2->y <= p->y)) ) { vt = (double)(p->y - v1->y) / (v2->y - v1->y); /* P->x x < v1->x + vt * (v2->x - v1->x)) { /* a valid crossing of y=p->y right of p->x */ ++cn; } } v1 = v2; } LWDEBUGF(3, "pt_in_ring_2d returning %d", cn&1); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ } static int lw_seg_interact(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2) { double minq=FP_MIN(q1->x,q2->x); double maxq=FP_MAX(q1->x,q2->x); double minp=FP_MIN(p1->x,p2->x); double maxp=FP_MAX(p1->x,p2->x); if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) return LW_FALSE; minq=FP_MIN(q1->y,q2->y); maxq=FP_MAX(q1->y,q2->y); minp=FP_MIN(p1->y,p2->y); maxp=FP_MAX(p1->y,p2->y); if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) return LW_FALSE; return LW_TRUE; } /** ** @brief returns the kind of #CG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2) ** @param p1 start point of first straight linesegment ** @param p2 end point of first straight linesegment ** @param q1 start point of second line segment ** @param q2 end point of second line segment ** @return a #CG_SEGMENT_INTERSECTION_TYPE ** Returns one of ** SEG_ERROR = -1, ** SEG_NO_INTERSECTION = 0, ** SEG_COLINEAR = 1, ** SEG_CROSS_LEFT = 2, ** SEG_CROSS_RIGHT = 3, */ int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2) { int pq1, pq2, qp1, qp2; /* No envelope interaction => we are done. */ if (!lw_seg_interact(p1, p2, q1, p2)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of q on the same side of p? */ pq1=lw_segment_side(p1,p2,q1); pq2=lw_segment_side(p1,p2,q2); if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of p on the same side of q? */ qp1=lw_segment_side(q1,q2,p1); qp2=lw_segment_side(q1,q2,p2); if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) ) { return SEG_NO_INTERSECTION; } /* Nobody is on one side or another? Must be colinear. */ if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 ) { return SEG_COLINEAR; } /* ** When one end-point touches, the sidedness is determined by the ** location of the other end-point. Only touches by the first point ** will be considered "real" to avoid double counting. */ LWDEBUGF(4, "pq1=%.15g pq2=%.15g", pq1, pq2); LWDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2); /* Second point of p or q touches, it's not a crossing. */ if ( pq2 == 0 || qp2 == 0 ) { return SEG_NO_INTERSECTION; } /* First point of p touches, it's a "crossing". */ if ( pq1 == 0 ) { if ( pq2 > 0 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* First point of q touches, it's a crossing. */ if ( qp1 == 0 ) { if ( pq1 < pq2 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* The segments cross, what direction is the crossing? */ if ( pq1 < pq2 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; /* This should never happen! */ return SEG_ERROR; } /** ** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior of 2 linestrings ** @param l1 first line string ** @param l2 second line string ** @return a #CG_LINE_CROSS_TYPE ** LINE_NO_CROSS = 0 ** LINE_CROSS_LEFT = -1 ** LINE_CROSS_RIGHT = 1 ** LINE_MULTICROSS_END_LEFT = -2 ** LINE_MULTICROSS_END_RIGHT = 2 ** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3 ** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 ** */ int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2) { uint32_t i = 0, j = 0; const POINT2D *p1, *p2, *q1, *q2; POINTARRAY *pa1 = NULL, *pa2 = NULL; int cross_left = 0; int cross_right = 0; int first_cross = 0; int this_cross = 0; #if POSTGIS_DEBUG_LEVEL >= 4 char *geom_ewkt; #endif pa1 = (POINTARRAY*)l1->points; pa2 = (POINTARRAY*)l2->points; /* One-point lines can't intersect (and shouldn't exist). */ if ( pa1->npoints < 2 || pa2->npoints < 2 ) return LINE_NO_CROSS; #if POSTGIS_DEBUG_LEVEL >= 4 geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l1); LWDEBUGF(4, "l1 = %s", geom_ewkt); lwfree(geom_ewkt); geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l2); LWDEBUGF(4, "l2 = %s", geom_ewkt); lwfree(geom_ewkt); #endif /* Initialize first point of q */ q1 = getPoint2d_cp(pa2, 0); for ( i = 1; i < pa2->npoints; i++ ) { /* Update second point of q to next value */ q2 = getPoint2d_cp(pa2, i); /* Initialize first point of p */ p1 = getPoint2d_cp(pa1, 0); for ( j = 1; j < pa1->npoints; j++ ) { /* Update second point of p to next value */ p2 = getPoint2d_cp(pa1, j); this_cross = lw_segment_intersects(p1, p2, q1, q2); LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1->x, p1->y, p2->x, p2->y); if ( this_cross == SEG_CROSS_LEFT ) { LWDEBUG(4,"this_cross == SEG_CROSS_LEFT"); cross_left++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } if ( this_cross == SEG_CROSS_RIGHT ) { LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT"); cross_right++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } /* ** Crossing at a co-linearity can be turned handled by extending ** segment to next vertex and seeing if the end points straddle ** the co-linear segment. */ if ( this_cross == SEG_COLINEAR ) { LWDEBUG(4,"this_cross == SEG_COLINEAR"); /* TODO: Add logic here and in segment_intersects() continue; */ } LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION"); /* Turn second point of p into first point */ p1 = p2; } /* Turn second point of q into first point */ q1 = q2; } LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right); if ( !cross_left && !cross_right ) return LINE_NO_CROSS; if ( !cross_left && cross_right == 1 ) return LINE_CROSS_RIGHT; if ( !cross_right && cross_left == 1 ) return LINE_CROSS_LEFT; if ( cross_left - cross_right == 1 ) return LINE_MULTICROSS_END_LEFT; if ( cross_left - cross_right == -1 ) return LINE_MULTICROSS_END_RIGHT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT ) return LINE_MULTICROSS_END_SAME_FIRST_LEFT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT ) return LINE_MULTICROSS_END_SAME_FIRST_RIGHT; return LINE_NO_CROSS; } static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; /* ** Calculate the geohash, iterating downwards and gaining precision. ** From geohash-native.c, (c) 2008 David Troy ** Released under the MIT License. */ char *geohash_point(double longitude, double latitude, int precision) { int is_even=1, i=0; double lat[2], lon[2], mid; char bits[] = {16,8,4,2,1}; int bit=0, ch=0; char *geohash = NULL; geohash = lwalloc(precision + 1); lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; while (i < precision) { if (is_even) { mid = (lon[0] + lon[1]) / 2; if (longitude >= mid) { ch |= bits[bit]; lon[0] = mid; } else { lon[1] = mid; } } else { mid = (lat[0] + lat[1]) / 2; if (latitude >= mid) { ch |= bits[bit]; lat[0] = mid; } else { lat[1] = mid; } } is_even = !is_even; if (bit < 4) { bit++; } else { geohash[i++] = base32[ch]; bit = 0; ch = 0; } } geohash[i] = 0; return geohash; } /* ** Calculate the geohash, iterating downwards and gaining precision. ** From geohash-native.c, (c) 2008 David Troy ** Released under the MIT License. */ unsigned int geohash_point_as_int(POINT2D *pt) { int is_even=1; double lat[2], lon[2], mid; int bit=32; unsigned int ch = 0; double longitude = pt->x; double latitude = pt->y; lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; while (--bit >= 0) { if (is_even) { mid = (lon[0] + lon[1]) / 2; if (longitude > mid) { ch |= 0x0001u << bit; lon[0] = mid; } else { lon[1] = mid; } } else { mid = (lat[0] + lat[1]) / 2; if (latitude > mid) { ch |= 0x0001 << bit; lat[0] = mid; } else { lat[1] = mid; } } is_even = !is_even; } return ch; } /* ** Decode a GeoHash into a bounding box. The lat and lon arguments should ** both be passed as double arrays of length 2 at a minimum where the values ** set in them will be the southwest and northeast coordinates of the bounding ** box accordingly. A precision less than 0 indicates that the entire length ** of the GeoHash should be used. ** It will call `lwerror` if an invalid character is found */ void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision) { bool is_even = 1; lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; size_t hashlen = strlen(geohash); if (precision < 0 || (size_t)precision > hashlen) { precision = (int)hashlen; } for (int i = 0; i < precision; i++) { char c = tolower(geohash[i]); /* Valid characters are all digits in base32 */ char *base32_pos = strchr(base32, c); if (!base32_pos) { lwerror("%s: Invalid character '%c'", __func__, geohash[i]); return; } char cd = base32_pos - base32; for (size_t j = 0; j < 5; j++) { const char bits[] = {16, 8, 4, 2, 1}; char mask = bits[j]; if (is_even) { lon[!(cd & mask)] = (lon[0] + lon[1]) / 2; } else { lat[!(cd & mask)] = (lat[0] + lat[1]) / 2; } is_even = !is_even; } } } int lwgeom_geohash_precision(GBOX bbox, GBOX *bounds) { double minx, miny, maxx, maxy; double latmax, latmin, lonmax, lonmin; double lonwidth, latwidth; double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust; int precision = 0; /* Get the bounding box, return error if things don't work out. */ minx = bbox.xmin; miny = bbox.ymin; maxx = bbox.xmax; maxy = bbox.ymax; if ( minx == maxx && miny == maxy ) { /* It's a point. Doubles have 51 bits of precision. ** 2 * 51 / 5 == 20 */ return 20; } lonmin = -180.0; latmin = -90.0; lonmax = 180.0; latmax = 90.0; /* Shrink a world bounding box until one of the edges interferes with the ** bounds of our rectangle. */ while ( 1 ) { lonwidth = lonmax - lonmin; latwidth = latmax - latmin; latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0; if ( minx > lonmin + lonwidth / 2.0 ) { lonminadjust = lonwidth / 2.0; } else if ( maxx < lonmax - lonwidth / 2.0 ) { lonmaxadjust = -1 * lonwidth / 2.0; } if ( lonminadjust || lonmaxadjust ) { lonmin += lonminadjust; lonmax += lonmaxadjust; /* Each adjustment cycle corresponds to 2 bits of storage in the ** geohash. */ precision++; } else { break; } if ( miny > latmin + latwidth / 2.0 ) { latminadjust = latwidth / 2.0; } else if (maxy < latmax - latwidth / 2.0 ) { latmaxadjust = -1 * latwidth / 2.0; } /* Only adjust if adjustments are legal (we haven't crossed any edges). */ if ( latminadjust || latmaxadjust ) { latmin += latminadjust; latmax += latmaxadjust; /* Each adjustment cycle corresponds to 2 bits of storage in the ** geohash. */ precision++; } else { break; } } /* Save the edges of our bounds, in case someone cares later. */ bounds->xmin = lonmin; bounds->xmax = lonmax; bounds->ymin = latmin; bounds->ymax = latmax; /* Each geohash character (base32) can contain 5 bits of information. ** We are returning the precision in characters, so here we divide. */ return precision / 5; } /* ** Return a geohash string for the geometry. ** Where the precision is non-positive, calculate a precision based on the ** bounds of the feature. Big features have loose precision. ** Small features have tight precision. */ char *lwgeom_geohash(const LWGEOM *lwgeom, int precision) { GBOX gbox; GBOX gbox_bounds; double lat, lon; int result; gbox_init(&gbox); gbox_init(&gbox_bounds); result = lwgeom_calculate_gbox_cartesian(lwgeom, &gbox); if ( result == LW_FAILURE ) return NULL; /* Return error if we are being fed something outside our working bounds */ if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 ) { lwerror("Geohash requires inputs in decimal degrees, got (%g %g, %g %g).", gbox.xmin, gbox.ymin, gbox.xmax, gbox.ymax); return NULL; } /* What is the center of our geometry bounds? We'll use that to ** approximate location. */ lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2; lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2; if ( precision <= 0 ) { precision = lwgeom_geohash_precision(gbox, &gbox_bounds); } /* ** Return the geohash of the center, with a precision determined by the ** extent of the bounds. ** Possible change: return the point at the center of the precision bounds? */ return geohash_point(lon, lat, precision); } lwgeom/src/liblwgeom/effectivearea.c0000644000176200001440000003431413773172540017267 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2014 Nicklas Avén * **********************************************************************/ #include "effectivearea.h" EFFECTIVE_AREAS* initiate_effectivearea(const POINTARRAY *inpts) { LWDEBUG(2, "Entered initiate_effectivearea"); EFFECTIVE_AREAS *ea; ea=lwalloc(sizeof(EFFECTIVE_AREAS)); ea->initial_arealist = lwalloc(inpts->npoints*sizeof(areanode)); ea->res_arealist = lwalloc(inpts->npoints*sizeof(double)); ea->inpts=inpts; return ea; } void destroy_effectivearea(EFFECTIVE_AREAS *ea) { lwfree(ea->initial_arealist); lwfree(ea->res_arealist); lwfree(ea); } static MINHEAP initiate_minheap(int npoints) { MINHEAP tree; tree.key_array = lwalloc(npoints*sizeof(void*)); tree.maxSize=npoints; tree.usedSize=0; return tree; } static void destroy_minheap(MINHEAP tree) { lwfree(tree.key_array); } /** Calculate the area of a triangle in 2d */ static double triarea2d(const double *P1, const double *P2, const double *P3) { return fabs(0.5*((P1[0]-P2[0])*(P3[1]-P2[1])-(P1[1]-P2[1])*(P3[0]-P2[0]))); } /** Calculate the area of a triangle in 3d space */ static double triarea3d(const double *P1, const double *P2, const double *P3) { LWDEBUG(2, "Entered triarea3d"); double ax,bx,ay,by,az,bz,cx,cy,cz, area; ax=P1[0]-P2[0]; bx=P3[0]-P2[0]; ay=P1[1]-P2[1]; by=P3[1]-P2[1]; az=P1[2]-P2[2]; bz=P3[2]-P2[2]; cx = ay*bz - az*by; cy = az*bx - ax*bz; cz = ax*by - ay*bx; area = fabs(0.5*(sqrt(cx*cx+cy*cy+cz*cz))); return area; } /** We create the minheap by ordering the minheap array by the areas in the areanode structs that the minheap keys refer to */ static int cmpfunc (const void * a, const void * b) { double v1 = (*(areanode**)a)->area; double v2 = (*(areanode**)b)->area; /*qsort gives unpredictable results when comparing identical values. If two values is the same we force returning the last point in the point array. That way we get the same ordering on different machines and platforms*/ if (v1==v2) return (*(areanode**)a)-(*(areanode**)b); else return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0); } /** Sift Down */ static void down(MINHEAP *tree,areanode *arealist,int parent) { LWDEBUG(2, "Entered down"); areanode **treearray=tree->key_array; int left=parent*2+1; int right = left +1; void *tmp; int swap=parent; double leftarea=0; double rightarea=0; double parentarea=((areanode*) treearray[parent])->area; if(leftusedSize) { leftarea=((areanode*) treearray[left])->area; if(parentarea>leftarea) swap=left; } if(rightusedSize) { rightarea=((areanode*) treearray[right])->area; if(rightareaparent) { /*ok, we have to swap something*/ tmp=treearray[parent]; treearray[parent]=treearray[swap]; /*Update reference*/ ((areanode*) treearray[parent])->treeindex=parent; treearray[swap]=tmp; /*Update reference*/ ((areanode*) treearray[swap])->treeindex=swap; if(swapusedSize) down(tree,arealist,swap); } return; } /** Sift Up */ static void up(MINHEAP *tree, __attribute__((__unused__)) areanode *e,int c) { LWDEBUG(2, "Entered up"); void *tmp; areanode **treearray=tree->key_array; int parent=floor((c-1)/2); while(((areanode*) treearray[c])->area<((areanode*) treearray[parent])->area) { /*ok, we have to swap*/ tmp=treearray[parent]; treearray[parent]=treearray[c]; /*Update reference*/ ((areanode*) treearray[parent])->treeindex=parent; treearray[c]=tmp; /*Update reference*/ ((areanode*) treearray[c])->treeindex=c; c=parent; parent=floor((c-1)/2); } return; } /** Get a reference to the point with the smallest effective area from the root of the min heap */ static areanode* minheap_pop(MINHEAP *tree,areanode *arealist ) { LWDEBUG(2, "Entered minheap_pop"); areanode *res = tree->key_array[0]; /*put last value first*/ tree->key_array[0]=tree->key_array[(tree->usedSize)-1]; ((areanode*) tree->key_array[0])->treeindex=0; tree->usedSize--; down(tree,arealist,0); return res; } /** The member of the minheap at index idx is changed. Update the tree and make restore the heap property */ static void minheap_update(MINHEAP *tree,areanode *arealist , int idx) { areanode **treearray=tree->key_array; int parent=floor((idx-1)/2); if(((areanode*) treearray[idx])->area<((areanode*) treearray[parent])->area) up(tree,arealist,idx); else down(tree,arealist,idx); return; } /** To get the effective area, we have to check what area a point results in when all smaller areas are eliminated */ static void tune_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld) { LWDEBUG(2, "Entered tune_areas"); const double *P1; const double *P2; const double *P3; double area; int go_on=1; double check_order_min_area = 0; int npoints=ea->inpts->npoints; int i; int current, before_current, after_current; MINHEAP tree = initiate_minheap(npoints); int is3d = FLAGS_GET_Z(ea->inpts->flags); /*Add all keys (index in initial_arealist) into minheap array*/ for (i=0;iinitial_arealist+i; LWDEBUGF(2, "add nr %d, with area %lf, and %lf",i,ea->initial_arealist[i].area, tree.key_array[i]->area ); } tree.usedSize=npoints; /*order the keys by area, small to big*/ qsort(tree.key_array, npoints, sizeof(void*), cmpfunc); /*We have to put references to our tree in our point-list*/ for (i=0;itreeindex=i; LWDEBUGF(4,"Check ordering qsort gives, area=%lf and belong to point %d",((areanode*) tree.key_array[i])->area, tree.key_array[i]-ea->initial_arealist); } /*Ok, now we have a minHeap, just need to keep it*/ /*for (i=0;iinitial_arealist)-ea->initial_arealist; /*We have found the smallest area. That is the resulting effective area for the "current" point*/ if (ires_arealist[current]=ea->initial_arealist[current].area; else ea->res_arealist[current]=FLT_MAX; if(ea->res_arealist[current]res_arealist[current],check_order_min_area); check_order_min_area=ea->res_arealist[current]; /*The found smallest area point is now regarded as eliminated and we have to recalculate the area the adjacent (ignoring earlier eliminated points) points gives*/ /*FInd point before and after*/ before_current=ea->initial_arealist[current].prev; after_current=ea->initial_arealist[current].next; P2= (double*)getPoint_internal(ea->inpts, before_current); P3= (double*)getPoint_internal(ea->inpts, after_current); /*Check if point before current point is the first in the point array. */ if(before_current>0) { P1= (double*)getPoint_internal(ea->inpts, ea->initial_arealist[before_current].prev); if(is3d) area=triarea3d(P1, P2, P3); else area=triarea2d(P1, P2, P3); ea->initial_arealist[before_current].area = FP_MAX(area,ea->res_arealist[current]); minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[before_current].treeindex); } if(after_currentinpts, ea->initial_arealist[after_current].next); if(is3d) area=triarea3d(P1, P2, P3); else area=triarea2d(P1, P2, P3); ea->initial_arealist[after_current].area = FP_MAX(area,ea->res_arealist[current]); minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[after_current].treeindex); } /*rearrange the nodes so the eliminated point will be ingored on the next run*/ ea->initial_arealist[before_current].next = ea->initial_arealist[current].next; ea->initial_arealist[after_current].prev = ea->initial_arealist[current].prev; /*Check if we are finished*/ if((!set_area && ea->res_arealist[current]>=trshld) || (ea->initial_arealist[0].next==(npoints-1))) go_on=0; i++; }; destroy_minheap(tree); return; } /** We calculate the effective area for the first time */ void ptarray_calc_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld) { LWDEBUG(2, "Entered ptarray_calc_areas"); int i; int npoints=ea->inpts->npoints; int is3d = FLAGS_GET_Z(ea->inpts->flags); double area; const double *P1; const double *P2; const double *P3; P1 = (double*)getPoint_internal(ea->inpts, 0); P2 = (double*)getPoint_internal(ea->inpts, 1); /*The first and last point shall always have the maximum effective area. We use float max to not make trouble for bbox*/ ea->initial_arealist[0].area=ea->initial_arealist[npoints-1].area=FLT_MAX; ea->res_arealist[0]=ea->res_arealist[npoints-1]=FLT_MAX; ea->initial_arealist[0].next=1; ea->initial_arealist[0].prev=0; for (i=1;i<(npoints)-1;i++) { ea->initial_arealist[i].next=i+1; ea->initial_arealist[i].prev=i-1; P3 = (double*)getPoint_internal(ea->inpts, i+1); if(is3d) area=triarea3d(P1, P2, P3); else area=triarea2d(P1, P2, P3); LWDEBUGF(4,"Write area %lf to point %d on address %p",area,i,&(ea->initial_arealist[i].area)); ea->initial_arealist[i].area=area; P1=P2; P2=P3; } ea->initial_arealist[npoints-1].next=npoints-1; ea->initial_arealist[npoints-1].prev=npoints-2; for (i=1;i<(npoints)-1;i++) { ea->res_arealist[i]=FLT_MAX; } tune_areas(ea,avoid_collaps,set_area, trshld); return ; } static POINTARRAY * ptarray_set_effective_area(POINTARRAY *inpts,int avoid_collaps,int set_area, double trshld) { LWDEBUG(2, "Entered ptarray_set_effective_area"); uint32_t p; POINT4D pt; EFFECTIVE_AREAS *ea; POINTARRAY *opts; int set_m; if(set_area) set_m=1; else set_m=FLAGS_GET_M(inpts->flags); ea=initiate_effectivearea(inpts); opts = ptarray_construct_empty(FLAGS_GET_Z(inpts->flags), set_m, inpts->npoints); ptarray_calc_areas(ea,avoid_collaps,set_area,trshld); if(set_area) { /*Only return points with an effective area above the threshold*/ for (p=0;pinpts->npoints;p++) { if(ea->res_arealist[p]>=trshld) { pt=getPoint4d(ea->inpts, p); pt.m=ea->res_arealist[p]; ptarray_append_point(opts, &pt, LW_TRUE); } } } else { /*Only return points with an effective area above the threshold*/ for (p=0;pinpts->npoints;p++) { if(ea->res_arealist[p]>=trshld) { pt=getPoint4d(ea->inpts, p); ptarray_append_point(opts, &pt, LW_TRUE); } } } destroy_effectivearea(ea); return opts; } static LWLINE* lwline_set_effective_area(const LWLINE *iline,int set_area, double trshld) { LWDEBUG(2, "Entered lwline_set_effective_area"); /* Skip empty case or too small to simplify */ if( lwline_is_empty(iline) || iline->points->npoints<3) return lwline_clone(iline); int set_m; if(set_area) set_m=1; else set_m=FLAGS_GET_M(iline->flags); LWLINE *oline = lwline_construct_empty(iline->srid, FLAGS_GET_Z(iline->flags), set_m); oline = lwline_construct(iline->srid, NULL, ptarray_set_effective_area(iline->points,2,set_area,trshld)); oline->type = iline->type; return oline; } static LWPOLY* lwpoly_set_effective_area(const LWPOLY *ipoly,int set_area, double trshld) { LWDEBUG(2, "Entered lwpoly_set_effective_area"); uint32_t i; int set_m; int avoid_collapse=4; if(set_area) set_m=1; else set_m=FLAGS_GET_M(ipoly->flags); LWPOLY *opoly = lwpoly_construct_empty(ipoly->srid, FLAGS_GET_Z(ipoly->flags), set_m); if( lwpoly_is_empty(ipoly) ) return opoly; /* should we return NULL instead ? */ for (i = 0; i < ipoly->nrings; i++) { POINTARRAY *pa = ptarray_set_effective_area(ipoly->rings[i],avoid_collapse,set_area,trshld); /* Add ring to simplified polygon */ if(pa->npoints>=4) { if( lwpoly_add_ring(opoly,pa ) == LW_FAILURE ) return NULL; } /*Inner rings we allow to collapse and then we remove them*/ avoid_collapse=0; } opoly->type = ipoly->type; if( lwpoly_is_empty(opoly)) return NULL; return opoly; } static LWCOLLECTION* lwcollection_set_effective_area(const LWCOLLECTION *igeom,int set_area, double trshld) { LWDEBUG(2, "Entered lwcollection_set_effective_area"); uint32_t i; int set_m; if(set_area) set_m=1; else set_m=FLAGS_GET_M(igeom->flags); LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags), set_m); if( lwcollection_is_empty(igeom) ) return out; /* should we return NULL instead ? */ for( i = 0; i < igeom->ngeoms; i++ ) { LWGEOM *ngeom = lwgeom_set_effective_area(igeom->geoms[i],set_area,trshld); if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom); } return out; } LWGEOM* lwgeom_set_effective_area(const LWGEOM *igeom,int set_area, double trshld) { LWDEBUG(2, "Entered lwgeom_set_effective_area"); switch (igeom->type) { case POINTTYPE: case MULTIPOINTTYPE: return lwgeom_clone(igeom); case LINETYPE: return (LWGEOM*)lwline_set_effective_area((LWLINE*)igeom,set_area, trshld); case POLYGONTYPE: return (LWGEOM*)lwpoly_set_effective_area((LWPOLY*)igeom,set_area, trshld); case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return (LWGEOM*)lwcollection_set_effective_area((LWCOLLECTION *)igeom,set_area, trshld); default: lwerror("lwgeom_simplify: unsupported geometry type: %s",lwtype_name(igeom->type)); } return NULL; } lwgeom/src/liblwgeom/lwstroke.c0000644000176200001440000010427213773172540016351 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * Copyright (C) 2017 Sandro Santilli * Copyright (C) 2018 Daniel Baston * **********************************************************************/ #include #include #include #include #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 3*/ #include "lwgeom_log.h" #include "liblwgeom_internal.h" LWGEOM *pta_unstroke(const POINTARRAY *points, int32_t srid); LWGEOM* lwline_unstroke(const LWLINE *line); LWGEOM* lwpolygon_unstroke(const LWPOLY *poly); LWGEOM* lwmline_unstroke(const LWMLINE *mline); LWGEOM* lwmpolygon_unstroke(const LWMPOLY *mpoly); LWGEOM* lwcollection_unstroke(const LWCOLLECTION *c); LWGEOM* lwgeom_unstroke(const LWGEOM *geom); /* * Determines (recursively in the case of collections) whether the geometry * contains at least on arc geometry or segment. */ int lwgeom_has_arc(const LWGEOM *geom) { LWCOLLECTION *col; uint32_t i; LWDEBUG(2, "lwgeom_has_arc called."); switch (geom->type) { case POINTTYPE: case LINETYPE: case POLYGONTYPE: case TRIANGLETYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: return LW_FALSE; case CIRCSTRINGTYPE: case CURVEPOLYTYPE: case COMPOUNDTYPE: return LW_TRUE; /* It's a collection that MAY contain an arc */ default: col = (LWCOLLECTION *)geom; for (i=0; ingeoms; i++) { if (lwgeom_has_arc(col->geoms[i]) == LW_TRUE) return LW_TRUE; } return LW_FALSE; } } /******************************************************************************* * Begin curve segmentize functions ******************************************************************************/ static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3) { LWDEBUGF(4,"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3); /* Counter-clockwise sweep */ if ( a1 < a2 ) { if ( angle <= a2 ) return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1); else return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2); } /* Clockwise sweep */ else { if ( angle >= a2 ) return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2); else return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3); } } /* Compute the angle covered by a single segment such that * a given number of segments per quadrant is achieved. */ static double angle_increment_using_segments_per_quad(double tol) { double increment; int perQuad = rint(tol); // error out if tol != perQuad ? (not-round) if ( perQuad != tol ) { lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad); return -1; } if ( perQuad < 1 ) { lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad); return -1; } increment = fabs(M_PI_2 / perQuad); LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI); return increment; } /* Compute the angle covered by a single quadrant such that * the segment deviates from the arc by no more than a given * amount. */ static double angle_increment_using_max_deviation(double max_deviation, double radius) { double increment, halfAngle, maxErr; if ( max_deviation <= 0 ) { lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", max_deviation); return -1; } /* * Ref: https://en.wikipedia.org/wiki/Sagitta_(geometry) * * An arc "sagitta" (distance between middle point of arc and * middle point of corresponding chord) is defined as: * * sagitta = radius * ( 1 - cos( angle ) ); * * We want our sagitta to be at most "tolerance" long, * and we want to find out angle, so we use the inverse * formula: * * tol = radius * ( 1 - cos( angle ) ); * 1 - cos( angle ) = tol/radius * - cos( angle ) = tol/radius - 1 * cos( angle ) = - tol/radius + 1 * angle = acos( 1 - tol/radius ) * * Constraints: 1.0 - tol/radius must be between -1 and 1 * which means tol must be between 0 and 2 times * the radius, which makes sense as you cannot have a * sagitta bigger than twice the radius! * */ maxErr = max_deviation; if ( maxErr > radius * 2 ) { maxErr = radius * 2; LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, " "using arc-max 2 * radius == %g", max_deviation, maxErr); } do { halfAngle = acos( 1.0 - maxErr / radius ); /* TODO: avoid a loop here, going rather straight to * a minimum angle value */ if ( halfAngle != 0 ) break; LWDEBUGF(2, "lwarc_linearize: tolerance %g is too small for this arc" " to compute approximation angle, doubling it", maxErr); maxErr *= 2; } while(1); increment = 2 * halfAngle; LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", max_deviation, radius, halfAngle, increment, increment * 180 / M_PI); return increment; } /* Check that a given angle is positive and, if so, take * it to be the angle covered by a single segment. */ static double angle_increment_using_max_angle(double tol) { if ( tol <= 0 ) { lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol); return -1; } return tol; } /** * Segmentize an arc * * Does not add the final vertex * * @param to POINTARRAY to append segmentized vertices to * @param p1 first point defining the arc * @param p2 second point defining the arc * @param p3 third point defining the arc * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags LW_LINEARIZE_FLAGS * * @return number of points appended (0 if collinear), * or -1 on error (lwerror would be called). * */ static int lwarc_linearize(POINTARRAY *to, const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags) { POINT2D center; POINT2D *t1 = (POINT2D*)p1; POINT2D *t2 = (POINT2D*)p2; POINT2D *t3 = (POINT2D*)p3; POINT4D pt; int p2_side = 0; int clockwise = LW_TRUE; double radius; /* Arc radius */ double increment; /* Angle per segment */ double angle_shift = 0; double a1, a2, a3; POINTARRAY *pa; int is_circle = LW_FALSE; int points_added = 0; int reverse = 0; int segments = 0; LWDEBUG(2, "lwarc_linearize called."); LWDEBUGF(2, " curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)", t1->x, t1->y, t2->x, t2->y, t3->x, t3->y); p2_side = lw_segment_side(t1, t3, t2); LWDEBUGF(2, " p2 side is %d", p2_side); /* Force counterclockwise scan if SYMMETRIC operation is requested */ if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC ) { /* swap p1-p3 */ t1 = (POINT2D*)p3; t3 = (POINT2D*)p1; p1 = (POINT4D*)t1; p3 = (POINT4D*)t3; p2_side = 1; reverse = 1; } radius = lw_arc_center(t1, t2, t3, ¢er); LWDEBUGF(2, " center is POINT(%.15g %.15g) - radius:%g", center.x, center.y, radius); /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) is_circle = LW_TRUE; /* Negative radius signals straight line, p1/p2/p3 are collinear */ if ( (radius < 0.0 || p2_side == 0) && ! is_circle ) return 0; /* The side of the p1/p3 line that p2 falls on dictates the sweep direction from p1 to p3. */ if ( p2_side == -1 ) clockwise = LW_TRUE; else clockwise = LW_FALSE; /* Compute the increment (angle per segment) depending on * our tolerance type. */ switch(tolerance_type) { case LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD: increment = angle_increment_using_segments_per_quad(tol); break; case LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION: increment = angle_increment_using_max_deviation(tol, radius); break; case LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE: increment = angle_increment_using_max_angle(tol); break; default: lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type); return -1; } if (increment < 0) { /* Error occurred in increment calculation somewhere * (lwerror already called) */ return -1; } /* Angles of each point that defines the arc section */ a1 = atan2(p1->y - center.y, p1->x - center.x); a2 = atan2(p2->y - center.y, p2->x - center.x); a3 = atan2(p3->y - center.y, p3->x - center.x); LWDEBUGF(2, "lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)", a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI); /* Calculate total arc angle, in radians */ double total_angle = clockwise ? a1 - a3 : a3 - a1; if ( total_angle <= 0 ) total_angle += M_PI * 2; /* At extreme tolerance values (very low or very high, depending on * the semantic) we may cause our arc to collapse. In this case, * we want shrink the increment enough so that we get two segments * for a standard arc, or three segments for a complete circle. */ int min_segs = is_circle ? 3 : 2; segments = ceil(total_angle / increment); if (segments < min_segs) { segments = min_segs; increment = total_angle / min_segs; } if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC ) { LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg", total_angle * 180 / M_PI); if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE ) { /* Number of complete steps */ segments = trunc(total_angle / increment); /* Figure out the angle remainder, i.e. the amount of the angle * that is left after we can take no more complete angle * increments. */ double angle_remainder = total_angle - (increment * segments); /* Shift the starting angle by half of the remainder. This * will have the effect of evenly distributing the remainder * among the first and last segments in the arc. */ angle_shift = angle_remainder / 2.0; LWDEBUGF(2, "lwarc_linearize RETAIN_ANGLE operation requested - " "total angle %g, steps %d, increment %g, remainder %g", total_angle * 180 / M_PI, segments, increment * 180 / M_PI, angle_remainder * 180 / M_PI); } else { /* Number of segments in output */ segments = ceil(total_angle / increment); /* Tweak increment to be regular for all the arc */ increment = total_angle / segments; LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - " "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g", total_angle * 180 / M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y, segments, increment * 180 / M_PI); } } /* p2 on left side => clockwise sweep */ if ( clockwise ) { LWDEBUG(2, " Clockwise sweep"); increment *= -1; angle_shift *= -1; /* Adjust a3 down so we can decrement from a1 to a3 cleanly */ if ( a3 > a1 ) a3 -= 2.0 * M_PI; if ( a2 > a1 ) a2 -= 2.0 * M_PI; } /* p2 on right side => counter-clockwise sweep */ else { LWDEBUG(2, " Counterclockwise sweep"); /* Adjust a3 up so we can increment from a1 to a3 cleanly */ if ( a3 < a1 ) a3 += 2.0 * M_PI; if ( a2 < a1 ) a2 += 2.0 * M_PI; } /* Override angles for circle case */ if( is_circle ) { increment = fabs(increment); segments = ceil(total_angle / increment); if (segments < 3) { segments = 3; increment = total_angle / 3; } a3 = a1 + 2.0 * M_PI; a2 = a1 + M_PI; clockwise = LW_FALSE; angle_shift = 0.0; } LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g", angle_shift * 180/M_PI, increment * 180/M_PI); if ( reverse ) { /* Append points in order to a temporary POINTARRAY and * reverse them before writing to the output POINTARRAY. */ const int capacity = 8; /* TODO: compute exactly ? */ pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity); } else { /* Append points directly to the output POINTARRAY, * starting with p1. */ pa = to; ptarray_append_point(pa, p1, LW_FALSE); ++points_added; } /* Sweep from a1 to a3 */ int seg_start = 1; /* First point is added manually */ int seg_end = segments; if (angle_shift != 0.0) { /* When we have extra angles we need to add the extra segments at the * start and end that cover those parts of the arc */ seg_start = 0; seg_end = segments + 1; } LWDEBUGF(2, "a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d", a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise); for (int s = seg_start; s < seg_end; s++) { double angle = a1 + increment * s + angle_shift; LWDEBUGF(2, " SA: %g ( %g deg )", angle, angle*180/M_PI); pt.x = center.x + radius * cos(angle); pt.y = center.y + radius * sin(angle); pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z); pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m); ptarray_append_point(pa, &pt, LW_FALSE); ++points_added; } /* Ensure the final point is EXACTLY the same as the first for the circular case */ if ( is_circle ) { ptarray_remove_point(pa, pa->npoints - 1); ptarray_append_point(pa, p1, LW_FALSE); } if ( reverse ) { int i; ptarray_append_point(to, p3, LW_FALSE); for ( i=pa->npoints; i>0; i-- ) { getPoint4d_p(pa, i-1, &pt); ptarray_append_point(to, &pt, LW_FALSE); } ptarray_free(pa); } return points_added; } /* * @param icurve input curve * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWLINE */ static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags) { LWLINE *oline; POINTARRAY *ptarray; uint32_t i, j; POINT4D p1, p2, p3, p4; int ret; LWDEBUGF(2, "lwcircstring_linearize called., dim = %d", icurve->points->flags); ptarray = ptarray_construct_empty(FLAGS_GET_Z(icurve->points->flags), FLAGS_GET_M(icurve->points->flags), 64); for (i = 2; i < icurve->points->npoints; i+=2) { LWDEBUGF(3, "lwcircstring_linearize: arc ending at point %d", i); getPoint4d_p(icurve->points, i - 2, &p1); getPoint4d_p(icurve->points, i - 1, &p2); getPoint4d_p(icurve->points, i, &p3); ret = lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags); if ( ret > 0 ) { LWDEBUGF(3, "lwcircstring_linearize: generated %d points", ptarray->npoints); } else if ( ret == 0 ) { LWDEBUG(3, "lwcircstring_linearize: points are colinear, returning curve points as line"); for (j = i - 2 ; j < i ; j++) { getPoint4d_p(icurve->points, j, &p4); ptarray_append_point(ptarray, &p4, LW_TRUE); } } else { /* An error occurred, lwerror should have been called by now */ ptarray_free(ptarray); return NULL; } } getPoint4d_p(icurve->points, icurve->points->npoints-1, &p1); ptarray_append_point(ptarray, &p1, LW_FALSE); oline = lwline_construct(icurve->srid, NULL, ptarray); return oline; } /* * @param icompound input compound curve * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWLINE */ static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags) { LWGEOM *geom; POINTARRAY *ptarray = NULL; LWLINE *tmp = NULL; uint32_t i, j; POINT4D p; LWDEBUG(2, "lwcompound_stroke called."); ptarray = ptarray_construct_empty(FLAGS_GET_Z(icompound->flags), FLAGS_GET_M(icompound->flags), 64); for (i = 0; i < icompound->ngeoms; i++) { geom = icompound->geoms[i]; if (geom->type == CIRCSTRINGTYPE) { tmp = lwcircstring_linearize((LWCIRCSTRING *)geom, tol, tolerance_type, flags); for (j = 0; j < tmp->points->npoints; j++) { getPoint4d_p(tmp->points, j, &p); ptarray_append_point(ptarray, &p, LW_TRUE); } lwline_free(tmp); } else if (geom->type == LINETYPE) { tmp = (LWLINE *)geom; for (j = 0; j < tmp->points->npoints; j++) { getPoint4d_p(tmp->points, j, &p); ptarray_append_point(ptarray, &p, LW_TRUE); } } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); return NULL; } } ptarray_remove_repeated_points_in_place(ptarray, 0.0, 2); return lwline_construct(icompound->srid, NULL, ptarray); } /* * @param icompound input curve polygon * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWPOLY */ static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags) { LWPOLY *ogeom; LWGEOM *tmp; LWLINE *line; POINTARRAY **ptarray; uint32_t i; LWDEBUG(2, "lwcurvepoly_linearize called."); ptarray = lwalloc(sizeof(POINTARRAY *)*curvepoly->nrings); for (i = 0; i < curvepoly->nrings; i++) { tmp = curvepoly->rings[i]; if (tmp->type == CIRCSTRINGTYPE) { line = lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, tolerance_type, flags); ptarray[i] = ptarray_clone_deep(line->points); lwline_free(line); } else if (tmp->type == LINETYPE) { line = (LWLINE *)tmp; ptarray[i] = ptarray_clone_deep(line->points); } else if (tmp->type == COMPOUNDTYPE) { line = lwcompound_linearize((LWCOMPOUND *)tmp, tol, tolerance_type, flags); ptarray[i] = ptarray_clone_deep(line->points); lwline_free(line); } else { lwerror("Invalid ring type found in CurvePoly."); return NULL; } } ogeom = lwpoly_construct(curvepoly->srid, NULL, curvepoly->nrings, ptarray); return ogeom; } /* Kept for backward compatibility - TODO: drop */ LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad) { return lwcurvepoly_linearize(curvepoly, perQuad, LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD, 0); } /** * @param mcurve input compound curve * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWMLINE */ static LWMLINE * lwmcurve_linearize(const LWMCURVE *mcurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags) { LWMLINE *ogeom; LWGEOM **lines; uint32_t i; LWDEBUGF(2, "lwmcurve_linearize called, geoms=%d, dim=%d.", mcurve->ngeoms, FLAGS_NDIMS(mcurve->flags)); lines = lwalloc(sizeof(LWGEOM *)*mcurve->ngeoms); for (i = 0; i < mcurve->ngeoms; i++) { const LWGEOM *tmp = mcurve->geoms[i]; if (tmp->type == CIRCSTRINGTYPE) { lines[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags); } else if (tmp->type == LINETYPE) { lines[i] = (LWGEOM *)lwline_construct(mcurve->srid, NULL, ptarray_clone_deep(((LWLINE *)tmp)->points)); } else if (tmp->type == COMPOUNDTYPE) { lines[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags); } else { lwerror("Unsupported geometry found in MultiCurve."); return NULL; } } ogeom = (LWMLINE *)lwcollection_construct(MULTILINETYPE, mcurve->srid, NULL, mcurve->ngeoms, lines); return ogeom; } /** * @param msurface input multi surface * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWMPOLY */ static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags) { LWMPOLY *ogeom; LWGEOM *tmp; LWPOLY *poly; LWGEOM **polys; POINTARRAY **ptarray; uint32_t i, j; LWDEBUG(2, "lwmsurface_linearize called."); polys = lwalloc(sizeof(LWGEOM *)*msurface->ngeoms); for (i = 0; i < msurface->ngeoms; i++) { tmp = msurface->geoms[i]; if (tmp->type == CURVEPOLYTYPE) { polys[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags); } else if (tmp->type == POLYGONTYPE) { poly = (LWPOLY *)tmp; ptarray = lwalloc(sizeof(POINTARRAY *)*poly->nrings); for (j = 0; j < poly->nrings; j++) { ptarray[j] = ptarray_clone_deep(poly->rings[j]); } polys[i] = (LWGEOM *)lwpoly_construct(msurface->srid, NULL, poly->nrings, ptarray); } } ogeom = (LWMPOLY *)lwcollection_construct(MULTIPOLYGONTYPE, msurface->srid, NULL, msurface->ngeoms, polys); return ogeom; } /** * @param collection input geometry collection * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags see flags in lwarc_linearize * * @return a newly allocated LWCOLLECTION */ static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags) { LWCOLLECTION *ocol; LWGEOM *tmp; LWGEOM **geoms; uint32_t i; LWDEBUG(2, "lwcollection_linearize called."); geoms = lwalloc(sizeof(LWGEOM *)*collection->ngeoms); for (i=0; ingeoms; i++) { tmp = collection->geoms[i]; switch (tmp->type) { case CIRCSTRINGTYPE: geoms[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags); break; case COMPOUNDTYPE: geoms[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags); break; case CURVEPOLYTYPE: geoms[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags); break; case MULTICURVETYPE: case MULTISURFACETYPE: case COLLECTIONTYPE: geoms[i] = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)tmp, tol, type, flags); break; default: geoms[i] = lwgeom_clone_deep(tmp); break; } } ocol = lwcollection_construct(COLLECTIONTYPE, collection->srid, NULL, collection->ngeoms, geoms); return ocol; } LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags) { LWGEOM * ogeom = NULL; switch (geom->type) { case CIRCSTRINGTYPE: ogeom = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)geom, tol, type, flags); break; case COMPOUNDTYPE: ogeom = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)geom, tol, type, flags); break; case CURVEPOLYTYPE: ogeom = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)geom, tol, type, flags); break; case MULTICURVETYPE: ogeom = (LWGEOM *)lwmcurve_linearize((LWMCURVE *)geom, tol, type, flags); break; case MULTISURFACETYPE: ogeom = (LWGEOM *)lwmsurface_linearize((LWMSURFACE *)geom, tol, type, flags); break; case COLLECTIONTYPE: ogeom = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)geom, tol, type, flags); break; default: ogeom = lwgeom_clone_deep(geom); } return ogeom; } /* Kept for backward compatibility - TODO: drop */ LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad) { return lwcurve_linearize(geom, perQuad, LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD, 0); } /** * Return ABC angle in radians * TODO: move to lwalgorithm */ static double lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c) { POINT2D ab, cb; ab.x = b->x - a->x; ab.y = b->y - a->y; cb.x = b->x - c->x; cb.y = b->y - c->y; double dot = (ab.x * cb.x + ab.y * cb.y); /* dot product */ double cross = (ab.x * cb.y - ab.y * cb.x); /* cross product */ double alpha = atan2(cross, dot); return alpha; } /** * Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within * that portion already described by a1/a2/a3 */ static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b) { POINT2D center; POINT2D *t1 = (POINT2D*)a1; POINT2D *t2 = (POINT2D*)a2; POINT2D *t3 = (POINT2D*)a3; POINT2D *tb = (POINT2D*)b; double radius = lw_arc_center(t1, t2, t3, ¢er); double b_distance, diff; /* Co-linear a1/a2/a3 */ if ( radius < 0.0 ) return LW_FALSE; b_distance = distance2d_pt_pt(tb, ¢er); diff = fabs(radius - b_distance); LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius); /* Is the point b on the circle? */ if ( diff < EPSILON_SQLMM ) { int a2_side = lw_segment_side(t1, t3, t2); int b_side = lw_segment_side(t1, t3, tb); double angle1 = lw_arc_angle(t1, t2, t3); double angle2 = lw_arc_angle(t2, t3, tb); /* Is the angle similar to the previous one ? */ diff = fabs(angle1 - angle2); LWDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff); if ( diff > EPSILON_SQLMM ) { return LW_FALSE; } /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */ /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */ if ( b_side != a2_side ) return LW_TRUE; } return LW_FALSE; } static LWGEOM * linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end) { int i = 0, j = 0; POINT4D p; POINTARRAY *pao = ptarray_construct(ptarray_has_z(pa), ptarray_has_m(pa), end-start+2); LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end); for( i = start; i < end + 2; i++ ) { getPoint4d_p(pa, i, &p); ptarray_set_point4d(pao, j++, &p); } return lwline_as_lwgeom(lwline_construct(srid, NULL, pao)); } static LWGEOM * circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end) { POINT4D p0, p1, p2; POINTARRAY *pao = ptarray_construct(ptarray_has_z(pa), ptarray_has_m(pa), 3); LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end); getPoint4d_p(pa, start, &p0); ptarray_set_point4d(pao, 0, &p0); getPoint4d_p(pa, (start+end+1)/2, &p1); ptarray_set_point4d(pao, 1, &p1); getPoint4d_p(pa, end+1, &p2); ptarray_set_point4d(pao, 2, &p2); return lwcircstring_as_lwgeom(lwcircstring_construct(srid, NULL, pao)); } static LWGEOM * geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end) { LWDEBUGF(4, "srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end); if ( is_arc ) return circstring_from_pa(pa, srid, start, end); else return linestring_from_pa(pa, srid, start, end); } LWGEOM * pta_unstroke(const POINTARRAY *points, int32_t srid) { int i = 0, j, k; POINT4D a1, a2, a3, b; POINT4D first, center; char *edges_in_arcs; int found_arc = LW_FALSE; int current_arc = 1; int num_edges; int edge_type; /* non-zero if edge is part of an arc */ int start, end; LWCOLLECTION *outcol; /* Minimum number of edges, per quadrant, required to define an arc */ const unsigned int min_quad_edges = 2; /* Die on null input */ if ( ! points ) lwerror("pta_unstroke called with null pointarray"); /* Null on empty input? */ if ( points->npoints == 0 ) return NULL; /* We can't desegmentize anything shorter than four points */ if ( points->npoints < 4 ) { /* Return a linestring here*/ lwerror("pta_unstroke needs implementation for npoints < 4"); } /* Allocate our result array of vertices that are part of arcs */ num_edges = points->npoints - 1; edges_in_arcs = lwalloc(num_edges + 1); memset(edges_in_arcs, 0, num_edges + 1); /* We make a candidate arc of the first two edges, */ /* And then see if the next edge follows it */ while( i < num_edges-2 ) { unsigned int arc_edges; double num_quadrants; double angle; found_arc = LW_FALSE; /* Make candidate arc */ getPoint4d_p(points, i , &a1); getPoint4d_p(points, i+1, &a2); getPoint4d_p(points, i+2, &a3); memcpy(&first, &a1, sizeof(POINT4D)); for( j = i+3; j < num_edges+1; j++ ) { LWDEBUGF(4, "i=%d, j=%d", i, j); getPoint4d_p(points, j, &b); /* Does this point fall on our candidate arc? */ if ( pt_continues_arc(&a1, &a2, &a3, &b) ) { /* Yes. Mark this edge and the two preceding it as arc components */ LWDEBUGF(4, "pt_continues_arc #%d", current_arc); found_arc = LW_TRUE; for ( k = j-1; k > j-4; k-- ) edges_in_arcs[k] = current_arc; } else { /* No. So we're done with this candidate arc */ LWDEBUG(4, "pt_continues_arc = false"); current_arc++; break; } memcpy(&a1, &a2, sizeof(POINT4D)); memcpy(&a2, &a3, sizeof(POINT4D)); memcpy(&a3, &b, sizeof(POINT4D)); } /* Jump past all the edges that were added to the arc */ if ( found_arc ) { /* Check if an arc was composed by enough edges to be * really considered an arc * See http://trac.osgeo.org/postgis/ticket/2420 */ arc_edges = j - 1 - i; LWDEBUGF(4, "arc defined by %d edges found", arc_edges); if ( first.x == b.x && first.y == b.y ) { LWDEBUG(4, "arc is a circle"); num_quadrants = 4; } else { lw_arc_center((POINT2D*)&first, (POINT2D*)&b, (POINT2D*)&a1, (POINT2D*)¢er); angle = lw_arc_angle((POINT2D*)&first, (POINT2D*)¢er, (POINT2D*)&b); int p2_side = lw_segment_side((POINT2D*)&first, (POINT2D*)&a1, (POINT2D*)&b); if ( p2_side >= 0 ) angle = -angle; if ( angle < 0 ) angle = 2 * M_PI + angle; num_quadrants = ( 4 * angle ) / ( 2 * M_PI ); LWDEBUGF(4, "arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quadrants:%g", first.x, first.y, center.x, center.y, b.x, b.y, angle, p2_side, num_quadrants); } /* a1 is first point, b is last point */ if ( arc_edges < min_quad_edges * num_quadrants ) { LWDEBUGF(4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants); for ( k = j-1; k >= i; k-- ) edges_in_arcs[k] = 0; } i = j-1; } else { /* Mark this edge as a linear edge */ edges_in_arcs[i] = 0; i = i+1; } } #if POSTGIS_DEBUG_LEVEL > 3 { char *edgestr = lwalloc(num_edges+1); for ( i = 0; i < num_edges; i++ ) { if ( edges_in_arcs[i] ) edgestr[i] = 48 + edges_in_arcs[i]; else edgestr[i] = '.'; } edgestr[num_edges] = 0; LWDEBUGF(3, "edge pattern %s", edgestr); lwfree(edgestr); } #endif start = 0; edge_type = edges_in_arcs[0]; outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points)); for( i = 1; i < num_edges; i++ ) { if( edge_type != edges_in_arcs[i] ) { end = i - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); start = i; edge_type = edges_in_arcs[i]; } } lwfree(edges_in_arcs); /* not needed anymore */ /* Roll out last item */ end = num_edges - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); /* Strip down to singleton if only one entry */ if ( outcol->ngeoms == 1 ) { LWGEOM *outgeom = outcol->geoms[0]; outcol->ngeoms = 0; lwcollection_free(outcol); return outgeom; } return lwcollection_as_lwgeom(outcol); } LWGEOM * lwline_unstroke(const LWLINE *line) { LWDEBUG(2, "lwline_unstroke called."); if ( line->points->npoints < 4 ) return lwline_as_lwgeom(lwline_clone_deep(line)); else return pta_unstroke(line->points, line->srid); } LWGEOM * lwpolygon_unstroke(const LWPOLY *poly) { LWGEOM **geoms; uint32_t i, hascurve = 0; LWDEBUG(2, "lwpolygon_unstroke called."); geoms = lwalloc(sizeof(LWGEOM *)*poly->nrings); for (i=0; inrings; i++) { geoms[i] = pta_unstroke(poly->rings[i], poly->srid); if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; inrings; i++) { lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */ } return lwgeom_clone_deep((LWGEOM *)poly); } return (LWGEOM *)lwcollection_construct(CURVEPOLYTYPE, poly->srid, NULL, poly->nrings, geoms); } LWGEOM * lwmline_unstroke(const LWMLINE *mline) { LWGEOM **geoms; uint32_t i, hascurve = 0; LWDEBUG(2, "lwmline_unstroke called."); geoms = lwalloc(sizeof(LWGEOM *)*mline->ngeoms); for (i=0; ingeoms; i++) { geoms[i] = lwline_unstroke((LWLINE *)mline->geoms[i]); if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; ingeoms; i++) { lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */ } return lwgeom_clone_deep((LWGEOM *)mline); } return (LWGEOM *)lwcollection_construct(MULTICURVETYPE, mline->srid, NULL, mline->ngeoms, geoms); } LWGEOM * lwmpolygon_unstroke(const LWMPOLY *mpoly) { LWGEOM **geoms; uint32_t i, hascurve = 0; LWDEBUG(2, "lwmpoly_unstroke called."); geoms = lwalloc(sizeof(LWGEOM *)*mpoly->ngeoms); for (i=0; ingeoms; i++) { geoms[i] = lwpolygon_unstroke((LWPOLY *)mpoly->geoms[i]); if (geoms[i]->type == CURVEPOLYTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; ingeoms; i++) { lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */ } return lwgeom_clone_deep((LWGEOM *)mpoly); } return (LWGEOM *)lwcollection_construct(MULTISURFACETYPE, mpoly->srid, NULL, mpoly->ngeoms, geoms); } LWGEOM * lwcollection_unstroke(const LWCOLLECTION *c) { LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION)); memcpy(ret, c, sizeof(LWCOLLECTION)); if (c->ngeoms > 0) { uint32_t i; ret->geoms = lwalloc(sizeof(LWGEOM *)*c->ngeoms); for (i=0; i < c->ngeoms; i++) { ret->geoms[i] = lwgeom_unstroke(c->geoms[i]); } if (c->bbox) { ret->bbox = gbox_copy(c->bbox); } } else { ret->bbox = NULL; ret->geoms = NULL; } return (LWGEOM *)ret; } LWGEOM * lwgeom_unstroke(const LWGEOM *geom) { LWDEBUG(2, "lwgeom_unstroke called."); switch (geom->type) { case LINETYPE: return lwline_unstroke((LWLINE *)geom); case POLYGONTYPE: return lwpolygon_unstroke((LWPOLY *)geom); case MULTILINETYPE: return lwmline_unstroke((LWMLINE *)geom); case MULTIPOLYGONTYPE: return lwmpolygon_unstroke((LWMPOLY *)geom); case COLLECTIONTYPE: return lwcollection_unstroke((LWCOLLECTION *)geom); default: return lwgeom_clone_deep(geom); } } lwgeom/src/liblwgeom/bytebuffer.c0000644000176200001440000002252513773172540016634 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Nicklas Avén * **********************************************************************/ #include "liblwgeom_internal.h" #include "bytebuffer.h" /** * Allocate just the internal buffer of an existing bytebuffer_t * struct. Useful for allocating short-lived bytebuffers off the stack. */ void bytebuffer_init_with_size(bytebuffer_t *s, size_t size) { if ( size < BYTEBUFFER_STATICSIZE ) { s->capacity = BYTEBUFFER_STATICSIZE; s->buf_start = s->buf_static; } else { s->buf_start = lwalloc(size); s->capacity = size; } s->readcursor = s->writecursor = s->buf_start; memset(s->buf_start, 0, s->capacity); } /** * Free the bytebuffer_t and all memory managed within it. */ void bytebuffer_destroy_buffer(bytebuffer_t *s) { if ( s->buf_start != s->buf_static ) { lwfree(s->buf_start); s->buf_start = NULL; } return; } /** * If necessary, expand the bytebuffer_t internal buffer to accomodate the * specified additional size. */ static inline void bytebuffer_makeroom(bytebuffer_t *s, size_t size_to_add) { LWDEBUGF(2,"Entered bytebuffer_makeroom with space need of %d", size_to_add); size_t current_write_size = (s->writecursor - s->buf_start); size_t capacity = s->capacity; size_t required_size = current_write_size + size_to_add; LWDEBUGF(2,"capacity = %d and required size = %d",capacity ,required_size); while (capacity < required_size) capacity *= 2; if ( capacity > s->capacity ) { size_t current_read_size = (s->readcursor - s->buf_start); LWDEBUGF(4,"We need to realloc more memory. New capacity is %d", capacity); if ( s->buf_start == s->buf_static ) { s->buf_start = lwalloc(capacity); memcpy(s->buf_start, s->buf_static, s->capacity); } else { s->buf_start = lwrealloc(s->buf_start, capacity); } s->capacity = capacity; s->writecursor = s->buf_start + current_write_size; s->readcursor = s->buf_start + current_read_size; } return; } /** Returns a copy of the internal buffer */ uint8_t* bytebuffer_get_buffer_copy(const bytebuffer_t *s, size_t *buffer_length) { size_t bufsz = bytebuffer_getlength(s); uint8_t *buf = lwalloc(bufsz); memcpy(buf, s->buf_start, bufsz); if ( buffer_length ) *buffer_length = bufsz; return buf; } /** Returns a read-only reference to the internal buffer */ const uint8_t* bytebuffer_get_buffer(const bytebuffer_t *s, size_t *buffer_length) { if ( buffer_length ) *buffer_length = bytebuffer_getlength(s); return s->buf_start; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_byte(bytebuffer_t *s, const uint8_t val) { LWDEBUGF(2,"Entered bytebuffer_append_byte with value %d", val); bytebuffer_makeroom(s, 1); *(s->writecursor)=val; s->writecursor += 1; return; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_bytebuffer(bytebuffer_t *write_to,bytebuffer_t *write_from ) { LWDEBUG(2,"bytebuffer_append_bytebuffer"); size_t size = bytebuffer_getlength(write_from); bytebuffer_makeroom(write_to, size); memcpy(write_to->writecursor, write_from->buf_start, size); write_to->writecursor += size; return; } /** * Writes a signed varInt to the buffer */ void bytebuffer_append_varint(bytebuffer_t *b, const int64_t val) { bytebuffer_makeroom(b, 16); b->writecursor += varint_s64_encode_buf(val, b->writecursor); return; } /** * Writes a unsigned varInt to the buffer */ void bytebuffer_append_uvarint(bytebuffer_t *b, const uint64_t val) { bytebuffer_makeroom(b, 16); b->writecursor += varint_u64_encode_buf(val, b->writecursor); return; } /** * Returns the length of the current buffer */ size_t bytebuffer_getlength(const bytebuffer_t *s) { return (size_t)(s->writecursor - s->buf_start); } /* Unused functions */ #if 0 /** * Allocate a new bytebuffer_t. Use bytebuffer_destroy to free. */ bytebuffer_t* bytebuffer_create(void) { LWDEBUG(2,"Entered bytebuffer_create"); return bytebuffer_create_with_size(BYTEBUFFER_STARTSIZE); } /** * Allocate a new bytebuffer_t. Use bytebuffer_destroy to free. */ bytebuffer_t* bytebuffer_create_with_size(size_t size) { LWDEBUGF(2,"Entered bytebuffer_create_with_size %d", size); bytebuffer_t *s; s = lwalloc(sizeof(bytebuffer_t)); if ( size < BYTEBUFFER_STATICSIZE ) { s->capacity = BYTEBUFFER_STATICSIZE; s->buf_start = s->buf_static; } else { s->buf_start = lwalloc(size); s->capacity = size; } s->readcursor = s->writecursor = s->buf_start; memset(s->buf_start,0,s->capacity); LWDEBUGF(4,"We create a buffer on %p of %d bytes", s->buf_start, s->capacity); return s; } /** * Free the bytebuffer_t and all memory managed within it. */ void bytebuffer_destroy(bytebuffer_t *s) { bytebuffer_destroy_buffer(s); if ( s ) lwfree(s); return; } /** * Set the read cursor to the beginning */ void bytebuffer_reset_reading(bytebuffer_t *s) { s->readcursor = s->buf_start; } /** * Reset the bytebuffer_t. Useful for starting a fresh string * without the expense of freeing and re-allocating a new * bytebuffer_t. */ void bytebuffer_clear(bytebuffer_t *s) { s->readcursor = s->writecursor = s->buf_start; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_bulk(bytebuffer_t *s, void * start, size_t size) { LWDEBUGF(2,"bytebuffer_append_bulk with size %d",size); bytebuffer_makeroom(s, size); memcpy(s->writecursor, start, size); s->writecursor += size; return; } /* * Writes Integer to the buffer */ void bytebuffer_append_int(bytebuffer_t *buf, const int val, int swap) { LWDEBUGF(2,"Entered bytebuffer_append_int with value %d, swap = %d", val, swap); LWDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); char *iptr = (char*)(&val); int i = 0; if ( sizeof(int) != WKB_INT_SIZE ) { lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE); } bytebuffer_makeroom(buf, WKB_INT_SIZE); /* Machine/request arch mismatch, so flip byte order */ if ( swap) { LWDEBUG(4,"Ok, let's do the swaping thing"); for ( i = 0; i < WKB_INT_SIZE; i++ ) { *(buf->writecursor) = iptr[WKB_INT_SIZE - 1 - i]; buf->writecursor += 1; } } /* If machine arch and requested arch match, don't flip byte order */ else { LWDEBUG(4,"Ok, let's do the memcopying thing"); memcpy(buf->writecursor, iptr, WKB_INT_SIZE); buf->writecursor += WKB_INT_SIZE; } LWDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); return; } /** * Writes a float64 to the buffer */ void bytebuffer_append_double(bytebuffer_t *buf, const double val, int swap) { LWDEBUGF(2,"Entered bytebuffer_append_double with value %lf swap = %d", val, swap); LWDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); char *dptr = (char*)(&val); int i = 0; if ( sizeof(double) != WKB_DOUBLE_SIZE ) { lwerror("Machine double size is not %d bytes!", WKB_DOUBLE_SIZE); } bytebuffer_makeroom(buf, WKB_DOUBLE_SIZE); /* Machine/request arch mismatch, so flip byte order */ if ( swap ) { LWDEBUG(4,"Ok, let's do the swapping thing"); for ( i = 0; i < WKB_DOUBLE_SIZE; i++ ) { *(buf->writecursor) = dptr[WKB_DOUBLE_SIZE - 1 - i]; buf->writecursor += 1; } } /* If machine arch and requested arch match, don't flip byte order */ else { LWDEBUG(4,"Ok, let's do the memcopying thing"); memcpy(buf->writecursor, dptr, WKB_DOUBLE_SIZE); buf->writecursor += WKB_DOUBLE_SIZE; } LWDEBUG(4,"Return from bytebuffer_append_double"); return; } /** * Reads a signed varInt from the buffer */ int64_t bytebuffer_read_varint(bytebuffer_t *b) { size_t size; int64_t val = varint_s64_decode(b->readcursor, b->buf_start + b->capacity, &size); b->readcursor += size; return val; } /** * Reads a unsigned varInt from the buffer */ uint64_t bytebuffer_read_uvarint(bytebuffer_t *b) { size_t size; uint64_t val = varint_u64_decode(b->readcursor, b->buf_start + b->capacity, &size); b->readcursor += size; return val; } /** * Returns a new bytebuffer were both ingoing bytebuffers is merged. * Caller is responsible for freeing both incoming bytebuffers and resulting bytebuffer */ bytebuffer_t* bytebuffer_merge(bytebuffer_t **buff_array, int nbuffers) { size_t total_size = 0, current_size, acc_size = 0; int i; for ( i = 0; i < nbuffers; i++ ) { total_size += bytebuffer_getlength(buff_array[i]); } bytebuffer_t *res = bytebuffer_create_with_size(total_size); for ( i = 0; i < nbuffers; i++) { current_size = bytebuffer_getlength(buff_array[i]); memcpy(res->buf_start+acc_size, buff_array[i]->buf_start, current_size); acc_size += current_size; } res->writecursor = res->buf_start + total_size; res->readcursor = res->buf_start; return res; } #endif lwgeom/src/liblwgeom/lwkmeans.c0000644000176200001440000002176513773172540016325 0ustar liggesusers/*------------------------------------------------------------------------- * * Copyright (c) 2018, Darafei Praliaskouski * Copyright (c) 2016, Paul Ramsey * *------------------------------------------------------------------------*/ #include "liblwgeom_internal.h" /* * When clustering lists with NULL or EMPTY elements, they will get this as * their cluster number. (All the other clusters will be non-negative) */ #define KMEANS_NULL_CLUSTER -1 /* * If the algorithm doesn't converge within this number of iterations, * it will return with a failure error code. */ #define KMEANS_MAX_ITERATIONS 1000 static void update_r(POINT2D** objs, int* clusters, uint32_t n, POINT2D** centers, uint32_t k) { POINT2D* obj; unsigned int i; double distance, curr_distance; uint32_t cluster, curr_cluster; for (i = 0; i < n; i++) { obj = objs[i]; /* Don't try to cluster NULL objects, just add them to the "unclusterable cluster" */ if (!obj) { clusters[i] = KMEANS_NULL_CLUSTER; continue; } /* Initialize with distance to first cluster */ curr_distance = distance2d_sqr_pt_pt(obj, centers[0]); curr_cluster = 0; /* Check all other cluster centers and find the nearest */ for (cluster = 1; cluster < k; cluster++) { distance = distance2d_sqr_pt_pt(obj, centers[cluster]); if (distance < curr_distance) { curr_distance = distance; curr_cluster = cluster; } } /* Store the nearest cluster this object is in */ clusters[i] = (int) curr_cluster; } } static void update_means(POINT2D** objs, int* clusters, uint32_t n, POINT2D** centers, uint32_t* weights, uint32_t k) { uint32_t i; int cluster; memset(weights, 0, sizeof(uint32_t) * k); for (i = 0; i < k; i++) { centers[i]->x = 0.0; centers[i]->y = 0.0; } for (i = 0; i < n; i++) { cluster = clusters[i]; if (cluster != KMEANS_NULL_CLUSTER) { centers[cluster]->x += objs[i]->x; centers[cluster]->y += objs[i]->y; weights[cluster] += 1; } } for (i = 0; i < k; i++) { if (weights[i]) { centers[i]->x /= weights[i]; centers[i]->y /= weights[i]; } } } static int kmeans(POINT2D** objs, int* clusters, uint32_t n, POINT2D** centers, uint32_t k) { uint32_t i = 0; int* clusters_last; int converged = LW_FALSE; size_t clusters_sz = sizeof(int) * n; uint32_t* weights; weights = lwalloc(sizeof(int) * k); /* previous cluster state array */ clusters_last = lwalloc(clusters_sz); for (i = 0; i < KMEANS_MAX_ITERATIONS && !converged; i++) { LW_ON_INTERRUPT(break); /* store the previous state of the clustering */ memcpy(clusters_last, clusters, clusters_sz); update_r(objs, clusters, n, centers, k); update_means(objs, clusters, n, centers, weights, k); /* if all the cluster numbers are unchanged, we are at a stable solution */ converged = memcmp(clusters_last, clusters, clusters_sz) == 0; } lwfree(clusters_last); lwfree(weights); if (!converged) lwerror("%s did not converge after %d iterations", __func__, i); return converged; } static void kmeans_init(POINT2D **objs, uint32_t n, POINT2D **centers, POINT2D *centers_raw, uint32_t k) { double* distances; uint32_t p1 = 0, p2 = 0; uint32_t i, j; uint32_t duplicate_count = 1; /* a point is a duplicate of itself */ double max_dst = -1, current_distance; double dst_p1, dst_p2; /* k=0, k=1: "clustering" is just input validation */ assert(k > 1); /* k >= 2: find two distant points greedily */ for (i = 1; i < n; i++) { /* skip null */ if (!objs[i]) continue; /* reinit if first element happened to be null */ if (!objs[p1] && !objs[p2]) { p1 = i; p2 = i; continue; } /* if we found a larger distance, replace our choice */ dst_p1 = distance2d_sqr_pt_pt(objs[i], objs[p1]); dst_p2 = distance2d_sqr_pt_pt(objs[i], objs[p2]); if ((dst_p1 > max_dst) || (dst_p2 > max_dst)) { if (dst_p1 > dst_p2) { max_dst = dst_p1; p2 = i; } else { max_dst = dst_p2; p1 = i; } } if ((dst_p1 == 0) || (dst_p2 == 0)) duplicate_count++; } if (duplicate_count > 1) lwnotice( "%s: there are at least %u duplicate inputs, number of output clusters may be less than you requested", __func__, duplicate_count); /* by now two points should be found and non-same */ assert(p1 != p2 && objs[p1] && objs[p2] && max_dst >= 0); /* accept these two points */ centers_raw[0] = *((POINT2D *)objs[p1]); centers[0] = &(centers_raw[0]); centers_raw[1] = *((POINT2D *)objs[p2]); centers[1] = &(centers_raw[1]); if (k > 2) { /* array of minimum distance to a point from accepted cluster centers */ distances = lwalloc(sizeof(double) * n); /* initialize array with distance to first object */ for (j = 0; j < n; j++) { if (objs[j]) distances[j] = distance2d_sqr_pt_pt(objs[j], centers[0]); else distances[j] = -1; } distances[p1] = -1; distances[p2] = -1; /* loop i on clusters, skip 0 and 1 as found already */ for (i = 2; i < k; i++) { uint32_t candidate_center = 0; double max_distance = -DBL_MAX; /* loop j on objs */ for (j = 0; j < n; j++) { /* empty objs and accepted clusters are already marked with distance = -1 */ if (distances[j] < 0) continue; /* update minimal distance with previosuly accepted cluster */ current_distance = distance2d_sqr_pt_pt(objs[j], centers[i - 1]); if (current_distance < distances[j]) distances[j] = current_distance; /* greedily take a point that's farthest from any of accepted clusters */ if (distances[j] > max_distance) { candidate_center = j; max_distance = distances[j]; } } /* Checked earlier by counting entries on input, just in case */ assert(max_distance >= 0); /* accept candidate to centers */ distances[candidate_center] = -1; /* Copy the point coordinates into the initial centers array * Centers array is an array of pointers to points, not an array of points */ centers_raw[i] = *((POINT2D *)objs[candidate_center]); centers[i] = &(centers_raw[i]); } lwfree(distances); } } int* lwgeom_cluster_2d_kmeans(const LWGEOM** geoms, uint32_t n, uint32_t k) { uint32_t i; uint32_t num_centroids = 0; uint32_t num_non_empty = 0; LWGEOM** centroids; POINT2D* centers_raw; const POINT2D* cp; int result = LW_FALSE; /* An array of objects to be analyzed. * All NULL values will be returned in the KMEANS_NULL_CLUSTER. */ POINT2D** objs; /* An array of centers for the algorithm. */ POINT2D** centers; /* Array to fill in with cluster numbers. */ int* clusters; assert(k > 0); assert(n > 0); assert(geoms); if (n < k) { lwerror("%s: number of geometries is less than the number of clusters requested, not all clusters will get data", __func__); k = n; } /* We'll hold the temporary centroid objects here */ centroids = lwalloc(sizeof(LWGEOM*) * n); memset(centroids, 0, sizeof(LWGEOM*) * n); /* The vector of cluster means. We have to allocate a chunk of memory for these because we'll be mutating them * in the kmeans algorithm */ centers_raw = lwalloc(sizeof(POINT2D) * k); memset(centers_raw, 0, sizeof(POINT2D) * k); /* K-means configuration setup */ objs = lwalloc(sizeof(POINT2D*) * n); clusters = lwalloc(sizeof(int) * n); centers = lwalloc(sizeof(POINT2D*) * k); /* Clean the memory */ memset(objs, 0, sizeof(POINT2D*) * n); memset(clusters, 0, sizeof(int) * n); memset(centers, 0, sizeof(POINT2D*) * k); /* Prepare the list of object pointers for K-means */ for (i = 0; i < n; i++) { const LWGEOM* geom = geoms[i]; LWPOINT* lwpoint; /* Null/empty geometries get a NULL pointer, set earlier with memset */ if ((!geom) || lwgeom_is_empty(geom)) continue; /* If the input is a point, use its coordinates */ /* If its not a point, convert it to one via centroid */ if (lwgeom_get_type(geom) != POINTTYPE) { LWGEOM* centroid = lwgeom_centroid(geom); if ((!centroid)) continue; if (lwgeom_is_empty(centroid)) { lwgeom_free(centroid); continue; } centroids[num_centroids++] = centroid; lwpoint = lwgeom_as_lwpoint(centroid); } else lwpoint = lwgeom_as_lwpoint(geom); /* Store a pointer to the POINT2D we are interested in */ cp = getPoint2d_cp(lwpoint->point, 0); objs[i] = (POINT2D*)cp; num_non_empty++; } if (num_non_empty < k) { lwnotice("%s: number of non-empty geometries is less than the number of clusters requested, not all clusters will get data", __func__); k = num_non_empty; } if (k > 1) { kmeans_init(objs, n, centers, centers_raw, k); result = kmeans(objs, clusters, n, centers, k); } else { /* k=0: everything is unclusterable * k=1: mark up NULL and non-NULL */ for (i = 0; i < n; i++) { if (k == 0 || !objs[i]) clusters[i] = KMEANS_NULL_CLUSTER; else clusters[i] = 0; } result = LW_TRUE; } /* Before error handling, might as well clean up all the inputs */ lwfree(objs); lwfree(centers); lwfree(centers_raw); lwfree(centroids); /* Good result */ if (result) return clusters; /* Bad result, not going to need the answer */ lwfree(clusters); return NULL; } lwgeom/src/liblwgeom/gserialized.c0000644000176200001440000002540213773172540016776 0ustar liggesusers#include "liblwgeom_internal.h" #include "gserialized1.h" #include "gserialized2.h" /* First four bits don't change between v0 and v1 */ #define GFLAG_Z 0x01 #define GFLAG_M 0x02 #define GFLAG_BBOX 0x04 #define GFLAG_GEODETIC 0x08 /* v1 and v2 MUST share the same version bits */ #define GFLAG_VER_0 0x40 #define GFLAGS_GET_VERSION(gflags) (((gflags) & GFLAG_VER_0)>>6) /** * Read the flags from a #GSERIALIZED and return a standard lwflag * integer */ lwflags_t gserialized_get_lwflags(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_get_lwflags(g); else return gserialized1_get_lwflags(g); } /** * Copy a new bounding box into an existing gserialized. * If necessary a new #GSERIALIZED will be allocated. Test * that input != output before freeing input. */ GSERIALIZED *gserialized_set_gbox(GSERIALIZED *g, GBOX *gbox) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_set_gbox(g, gbox); else return gserialized1_set_gbox(g, gbox); } /** * Return the serialization version */ uint32_t gserialized_get_version(const GSERIALIZED *g) { return GFLAGS_GET_VERSION(g->gflags); } /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED* gserialized_drop_gbox(GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_drop_gbox(g); else return gserialized1_drop_gbox(g); } /** * Read the box from the #GSERIALIZED or calculate it if necessary. * Return #LWFAILURE if box cannot be calculated (NULL or EMPTY * input). */ int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_get_gbox_p(g, gbox); else return gserialized1_get_gbox_p(g, gbox); } /** * Read the box from the #GSERIALIZED or return #LWFAILURE if * box is unavailable. */ int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_fast_gbox_p(g, gbox); else return gserialized1_fast_gbox_p(g, gbox); } /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ uint32_t gserialized_get_type(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_get_type(g); else return gserialized1_get_type(g); } /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ uint32_t gserialized_max_header_size(void) { size_t sz1 = gserialized1_max_header_size(); size_t sz2 = gserialized2_max_header_size(); return sz1 > sz2 ? sz1 : sz2; } /** * Returns a hash code for the srid/type/geometry information * in the GSERIALIZED. Ignores metadata like flags and optional * boxes, etc. */ int32_t gserialized_hash(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_hash(g); else return gserialized1_hash(g); } /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ int32_t gserialized_get_srid(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_get_srid(g); else return gserialized1_get_srid(g); } /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ void gserialized_set_srid(GSERIALIZED *g, int32_t srid) { if (GFLAGS_GET_VERSION(g->gflags)) gserialized2_set_srid(g, srid); else gserialized1_set_srid(g, srid); } /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ int gserialized_is_empty(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_is_empty(g); else return gserialized1_is_empty(g); } /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ int gserialized_has_bbox(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_has_bbox(g); else return gserialized1_has_bbox(g); } /** * Check if a #GSERIALIZED has a Z ordinate. */ int gserialized_has_z(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_has_z(g); else return gserialized1_has_z(g); } /** * Check if a #GSERIALIZED has an M ordinate. */ int gserialized_has_m(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_has_m(g); else return gserialized1_has_m(g); } /** * Check if a #GSERIALIZED is a geography. */ int gserialized_is_geodetic(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_is_geodetic(g); else return gserialized1_is_geodetic(g); } /** * Return the number of dimensions (2, 3, 4) in a geometry */ int gserialized_ndims(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_ndims(g); else return gserialized1_ndims(g); } /** * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ GSERIALIZED* gserialized_from_lwgeom(LWGEOM *geom, size_t *size) { return gserialized2_from_lwgeom(geom, size); } /** * Return the memory size a GSERIALIZED will occupy for a given LWGEOM. */ size_t gserialized_from_lwgeom_size(const LWGEOM *geom) { return gserialized2_from_lwgeom_size(geom); } /** * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates * that are double aligned and suitable for direct reading using getPoint2d_p_ro */ LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g) { if (GFLAGS_GET_VERSION(g->gflags)) return lwgeom_from_gserialized2(g); else return lwgeom_from_gserialized1(g); } const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_get_float_box_p(g, ndims); else return gserialized1_get_float_box_p(g, ndims); } int gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point) { if (GFLAGS_GET_VERSION(g->gflags)) return gserialized2_peek_first_point(g, out_point); else return gserialized1_peek_first_point(g, out_point); } /** * Return -1 if g1 is "less than" g2, 1 if g1 is "greater than" * g2 and 0 if g1 and g2 are the "same". Equality is evaluated * with a memcmp and size check. So it is possible that two * identical objects where one lacks a bounding box could be * evaluated as non-equal initially. Greater and less than * are evaluated by calculating a sortable key from the center * point of the object bounds. * Because this function might have to handle GSERIALIZED * objects of either version, we implement it up here at the * switching layer rather than down lower. */ #define G2FLAG_EXTENDED 0x10 inline static size_t gserialized_header_size(const GSERIALIZED *g) { size_t sz = 8; /* varsize (4) + srid(3) + flags (1) */ if ((GFLAGS_GET_VERSION(g->gflags)) && (G2FLAG_EXTENDED & g->gflags)) sz += 8; if (GFLAG_BBOX & g->gflags) { if (GFLAG_GEODETIC & g->gflags) { sz += 6 * sizeof(float); } else { sz += 4 * sizeof(float) + ((GFLAG_Z & g->gflags) ? 2*sizeof(float) : 0) + ((GFLAG_M & g->gflags) ? 2*sizeof(float) : 0); } } return sz; } inline static int gserialized_cmp_srid(const GSERIALIZED *g1, const GSERIALIZED *g2) { return ( g1->srid[0] == g2->srid[0] && g1->srid[1] == g2->srid[1] && g1->srid[2] == g2->srid[2] ) ? 0 : 1; } /* ORDER BY hash(g), g::bytea, ST_SRID(g), hasz(g), hasm(g) */ int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2) { GBOX box1, box2; uint64_t hash1, hash2; size_t sz1 = SIZE_GET(g1->size); size_t sz2 = SIZE_GET(g2->size); size_t hsz1 = gserialized_header_size(g1); size_t hsz2 = gserialized_header_size(g2); uint8_t *b1 = (uint8_t*)g1 + hsz1; uint8_t *b2 = (uint8_t*)g2 + hsz2; size_t bsz1 = sz1 - hsz1; size_t bsz2 = sz2 - hsz2; size_t bsz_min = bsz1 < bsz2 ? bsz1 : bsz2; /* Equality fast path */ /* Return equality for perfect equality only */ int cmp_srid = gserialized_cmp_srid(g1, g2); int cmp = memcmp(b1, b2, bsz_min); int g1hasz = gserialized_has_z(g1); int g1hasm = gserialized_has_m(g1); int g2hasz = gserialized_has_z(g2); int g2hasm = gserialized_has_m(g2); if (bsz1 == bsz2 && cmp_srid == 0 && cmp == 0 && g1hasz == g2hasz && g1hasm == g2hasm) return 0; else { int g1_is_empty = (gserialized_get_gbox_p(g1, &box1) == LW_FAILURE); int g2_is_empty = (gserialized_get_gbox_p(g2, &box2) == LW_FAILURE); int32_t srid1 = gserialized_get_srid(g1); int32_t srid2 = gserialized_get_srid(g2); /* Empty < Non-empty */ if (g1_is_empty && !g2_is_empty) return -1; /* Non-empty > Empty */ if (!g1_is_empty && g2_is_empty) return 1; if (!g1_is_empty && !g2_is_empty) { /* Using the boxes, calculate sortable hash key. */ hash1 = gbox_get_sortable_hash(&box1, srid1); hash2 = gbox_get_sortable_hash(&box2, srid2); if (hash1 > hash2) return 1; if (hash1 < hash2) return -1; } /* Prefix comes before longer one. */ if (bsz1 != bsz2 && cmp == 0) { if (bsz1 < bsz2) return -1; else if (bsz1 > bsz2) return 1; } /* If SRID is not equal, sort on it */ if (cmp_srid != 0) return (srid1 > srid2) ? 1 : -1; /* ZM flag sort*/ if (g1hasz != g2hasz) return (g1hasz > g2hasz) ? 1 : -1; if (g1hasm != g2hasm) return (g1hasm > g2hasm) ? 1 : -1; assert(cmp != 0); return cmp > 0 ? 1 : -1; } } uint64_t gserialized_get_sortable_hash(const GSERIALIZED *g) { GBOX box; int is_empty = (gserialized_get_gbox_p(g, &box) == LW_FAILURE); if (is_empty) return 0; else return gbox_get_sortable_hash(&box, gserialized_get_srid(g)); } void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname); void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname) { int32_t srid1 = gserialized_get_srid(g1); int32_t srid2 = gserialized_get_srid(g2); if (srid1 != srid2) lwerror("%s: Operation on mixed SRID geometries (%s, %d) != (%s, %d)", funcname, lwtype_name(gserialized1_get_type(g1)), srid1, lwtype_name(gserialized_get_type(g2)), srid2); } void gserialized_error_if_srid_mismatch_reference(const GSERIALIZED *g1, const int32_t srid2, const char *funcname); void gserialized_error_if_srid_mismatch_reference(const GSERIALIZED *g1, const int32_t srid2, const char *funcname) { int32_t srid1 = gserialized_get_srid(g1); if (srid1 != srid2) lwerror("%s: Operation on mixed SRID geometries %s %d != %d", funcname, lwtype_name(gserialized1_get_type(g1)), srid1, srid2); } lwgeom/src/liblwgeom/varint.c0000644000176200001440000001201013773172540015766 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2014 Sandro Santilli * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #include "varint.h" #include "lwgeom_log.h" #include "liblwgeom.h" /* -------------------------------------------------------------------------------- */ static size_t _varint_u64_encode_buf(uint64_t val, uint8_t *buf) { uint8_t grp; uint64_t q = val; uint8_t *ptr = buf; while (1) { /* We put the 7 least significant bits in grp */ grp = 0x7f & q; /* We rightshift our input value 7 bits */ /* which means that the 7 next least significant bits */ /* becomes the 7 least significant */ q = q >> 7; /* Check if, after our rightshifting, we still have */ /* anything to read in our input value. */ if ( q > 0 ) { /* In the next line quite a lot is happening. */ /* Since there is more to read in our input value */ /* we signal that by setting the most significant bit */ /* in our byte to 1. */ /* Then we put that byte in our buffer and move the pointer */ /* forward one step */ *ptr = 0x80 | grp; ptr++; } else { /* The same as above, but since there is nothing more */ /* to read in our input value we leave the most significant bit unset */ *ptr = grp; ptr++; return ptr - buf; } } /* This cannot happen */ lwerror("%s: Got out of infinite loop. Consciousness achieved.", __func__); return (size_t)0; } size_t varint_u64_encode_buf(uint64_t val, uint8_t *buf) { return _varint_u64_encode_buf(val, buf); } size_t varint_u32_encode_buf(uint32_t val, uint8_t *buf) { return _varint_u64_encode_buf((uint64_t)val, buf); } size_t varint_s64_encode_buf(int64_t val, uint8_t *buf) { return _varint_u64_encode_buf(zigzag64(val), buf); } size_t varint_s32_encode_buf(int32_t val, uint8_t *buf) { return _varint_u64_encode_buf((uint64_t)zigzag32(val), buf); } /* Read from signed 64bit varint */ int64_t varint_s64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size) { return unzigzag64(varint_u64_decode(the_start, the_end, size)); } /* Read from unsigned 64bit varint */ uint64_t varint_u64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size) { uint64_t nVal = 0; int nShift = 0; uint8_t nByte; const uint8_t *ptr = the_start; /* Check so we don't read beyond the twkb */ while( ptr < the_end ) { nByte = *ptr; /* Hibit is set, so this isn't the last byte */ if (nByte & 0x80) { /* We get here when there is more to read in the input varInt */ /* Here we take the least significant 7 bits of the read */ /* byte and put it in the most significant place in the result variable. */ nVal |= ((uint64_t)(nByte & 0x7f)) << nShift; /* move the "cursor" of the input buffer step (8 bits) */ ptr++; /* move the cursor in the resulting variable (7 bits) */ nShift += 7; } else { /* move the "cursor" one step */ ptr++; /* Move the last read byte to the most significant */ /* place in the result and return the whole result */ *size = ptr - the_start; return nVal | ((uint64_t)nByte << nShift); } } lwerror("%s: varint extends past end of buffer", __func__); return 0; } size_t varint_size(const uint8_t *the_start, const uint8_t *the_end) { const uint8_t *ptr = the_start; /* Check so we don't read beyond the twkb */ while( ptr < the_end ) { /* Hibit is set, this isn't the last byte */ if (*ptr & 0x80) { ptr++; } else { ptr++; return ptr - the_start; } } return 0; } uint64_t zigzag64(int64_t val) { return val >= 0 ? ((uint64_t)val) << 1 : ((((uint64_t)(-1 - val)) << 1) | 0x01); } uint32_t zigzag32(int32_t val) { return val >= 0 ? ((uint32_t)val) << 1 : ((((uint32_t)(-1 - val)) << 1) | 0x01); } uint8_t zigzag8(int8_t val) { return val >= 0 ? ((uint8_t)val) << 1 : ((((uint8_t)(-1 - val)) << 1) | 0x01); } int64_t unzigzag64(uint64_t val) { return !(val & 0x01) ? ((int64_t)(val >> 1)) : (-1 * (int64_t)((val+1) >> 1)); } int32_t unzigzag32(uint32_t val) { return !(val & 0x01) ? ((int32_t)(val >> 1)) : (-1 * (int32_t)((val+1) >> 1)); } int8_t unzigzag8(uint8_t val) { return !(val & 0x01) ? ((int8_t)(val >> 1)) : (-1 * (int8_t)((val+1) >> 1)); } lwgeom/src/liblwgeom/measures3d.c0000644000176200001440000013405313773172540016552 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Nicklas Avén * Copyright 2019 Darafei Praliaskouski * **********************************************************************/ #include #include #include "measures3d.h" #include "lwrandom.h" #include "lwgeom_log.h" static inline int get_3dvector_from_points(POINT3DZ *p1, POINT3DZ *p2, VECTOR3D *v) { v->x = p2->x - p1->x; v->y = p2->y - p1->y; v->z = p2->z - p1->z; return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z)); } static inline int get_3dcross_product(VECTOR3D *v1, VECTOR3D *v2, VECTOR3D *v) { v->x = (v1->y * v2->z) - (v1->z * v2->y); v->y = (v1->z * v2->x) - (v1->x * v2->z); v->z = (v1->x * v2->y) - (v1->y * v2->x); return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z)); } /** This function is used to create a vertical line used for cases where one if the geometries lacks z-values. The vertical line crosses the 2d point that is closest and the z-range is from maxz to minz in the geometry that has z values. */ static LWGEOM * create_v_line(const LWGEOM *lwgeom, double x, double y, int32_t srid) { LWPOINT *lwpoints[2]; GBOX gbox; int rv = lwgeom_calculate_gbox(lwgeom, &gbox); if (rv == LW_FAILURE) return NULL; lwpoints[0] = lwpoint_make3dz(srid, x, y, gbox.zmin); lwpoints[1] = lwpoint_make3dz(srid, x, y, gbox.zmax); return (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); } LWGEOM * lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MIN); } LWGEOM * lwgeom_furthest_line_3d(LWGEOM *lw1, LWGEOM *lw2) { return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MAX); } LWGEOM * lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist3d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN); } /** Function initializing 3dshortestline and 3dlongestline calculations. */ LWGEOM * lw_dist3d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) { LWDEBUG(2, "lw_dist3d_distanceline is called"); double x1, x2, y1, y2, z1, z2, x, y; double initdistance = (mode == DIST_MIN ? DBL_MAX : -1.0); DISTPTS3D thedl; LWPOINT *lwpoints[2]; LWGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0.0; /* Check if we really have 3D geometries * If not, send it to 2D-calculations which will give the same result * as an infinite z-value at one or two of the geometries */ if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) { lwnotice( "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2)) return lw_dist2d_distanceline(lw1, lw2, srid, mode); DISTPTS thedl2d; thedl2d.mode = mode; thedl2d.distance = initdistance; thedl2d.tolerance = 0.0; if (!lw_dist2d_comp(lw1, lw2, &thedl2d)) { /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } LWGEOM *vertical_line; if (!lwgeom_has_z(lw1)) { x = thedl2d.p1.x; y = thedl2d.p1.y; vertical_line = create_v_line(lw2, x, y, srid); if (!lw_dist3d_recursive(vertical_line, lw2, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwfree(vertical_line); lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } lwfree(vertical_line); } if (!lwgeom_has_z(lw2)) { x = thedl2d.p2.x; y = thedl2d.p2.y; vertical_line = create_v_line(lw1, x, y, srid); if (!lw_dist3d_recursive(lw1, vertical_line, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwfree(vertical_line); lwerror("Some unspecified error."); return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } lwfree(vertical_line); } } else { if (!lw_dist3d_recursive(lw1, lw2, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } } /*if thedl.distance is unchanged there where only empty geometries input*/ if (thedl.distance == initdistance) { LWDEBUG(3, "didn't find geometries to measure between, returning null"); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } else { x1 = thedl.p1.x; y1 = thedl.p1.y; z1 = thedl.p1.z; x2 = thedl.p2.x; y2 = thedl.p2.y; z2 = thedl.p2.z; lwpoints[0] = lwpoint_make3dz(srid, x1, y1, z1); lwpoints[1] = lwpoint_make3dz(srid, x2, y2, z2); result = (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); } return result; } /** Function initializing 3dclosestpoint calculations. */ LWGEOM * lw_dist3d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) { double x, y, z; DISTPTS3D thedl; double initdistance = DBL_MAX; LWGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0; LWDEBUG(2, "lw_dist3d_distancepoint is called"); /* Check if we really have 3D geometries * If not, send it to 2D-calculations which will give the same result * as an infinite z-value at one or two of the geometries */ if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) { lwnotice( "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2)) return lw_dist2d_distancepoint(lw1, lw2, srid, mode); DISTPTS thedl2d; thedl2d.mode = mode; thedl2d.distance = initdistance; thedl2d.tolerance = 0.0; if (!lw_dist2d_comp(lw1, lw2, &thedl2d)) { /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } LWGEOM *vertical_line; if (!lwgeom_has_z(lw1)) { x = thedl2d.p1.x; y = thedl2d.p1.y; vertical_line = create_v_line(lw2, x, y, srid); if (!lw_dist3d_recursive(vertical_line, lw2, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwfree(vertical_line); lwerror("Some unspecified error."); return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } lwfree(vertical_line); } if (!lwgeom_has_z(lw2)) { x = thedl2d.p2.x; y = thedl2d.p2.y; vertical_line = create_v_line(lw1, x, y, srid); if (!lw_dist3d_recursive(lw1, vertical_line, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwfree(vertical_line); lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } lwfree(vertical_line); } } else { if (!lw_dist3d_recursive(lw1, lw2, &thedl)) { /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } } if (thedl.distance == initdistance) { LWDEBUG(3, "didn't find geometries to measure between, returning null"); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } else { x = thedl.p1.x; y = thedl.p1.y; z = thedl.p1.z; result = (LWGEOM *)lwpoint_make3dz(srid, x, y, z); } return result; } /** Function initializing 3d max distance calculation */ double lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2) { return lwgeom_maxdistance3d_tolerance(lw1, lw2, 0.0); } /** Function handling 3d max distance calculations and dfullywithin calculations. The difference is just the tolerance. */ double lwgeom_maxdistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) { if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) { lwnotice( "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); return lwgeom_maxdistance2d_tolerance(lw1, lw2, tolerance); } DISTPTS3D thedl; LWDEBUG(2, "lwgeom_maxdistance3d_tolerance is called"); thedl.mode = DIST_MAX; thedl.distance = -1; thedl.tolerance = tolerance; if (lw_dist3d_recursive(lw1, lw2, &thedl)) return thedl.distance; /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); return -1; } /** Function initializing 3d min distance calculation */ double lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2) { return lwgeom_mindistance3d_tolerance(lw1, lw2, 0.0); } static inline int gbox_contains_3d(const GBOX *g1, const GBOX *g2) { return (g2->xmin >= g1->xmin) && (g2->xmax <= g1->xmax) && (g2->ymin >= g1->ymin) && (g2->ymax <= g1->ymax) && (g2->zmin >= g1->zmin) && (g2->zmax <= g1->zmax); } static inline int lwgeom_solid_contains_lwgeom(const LWGEOM *solid, const LWGEOM *g) { const GBOX *b1; const GBOX *b2; /* If solid isn't solid it can't contain anything */ if (!FLAGS_GET_SOLID(solid->flags)) return LW_FALSE; b1 = lwgeom_get_bbox(solid); b2 = lwgeom_get_bbox(g); /* If box won't contain box, shape won't contain shape */ if (!gbox_contains_3d(b1, b2)) return LW_FALSE; else /* Raycast upwards in Z */ { POINT4D pt; /* We'll skew copies if we're not lucky */ LWGEOM *solid_copy = lwgeom_clone_deep(solid); LWGEOM *g_copy = lwgeom_clone_deep(g); while (LW_TRUE) { uint8_t is_boundary = LW_FALSE; uint8_t is_inside = LW_FALSE; /* take first point */ if (!lwgeom_startpoint(g_copy, &pt)) return LW_FALSE; /* get part of solid that is upwards */ LWCOLLECTION *c = lwgeom_clip_to_ordinate_range(solid_copy, 'Z', pt.z, DBL_MAX, 0); for (uint32_t i = 0; i < c->ngeoms; i++) { if (c->geoms[i]->type == POLYGONTYPE) { /* 3D raycast along Z is 2D point in polygon */ int t = lwpoly_contains_point((LWPOLY *)c->geoms[i], (POINT2D *)&pt); if (t == LW_INSIDE) is_inside = !is_inside; else if (t == LW_BOUNDARY) { is_boundary = LW_TRUE; break; } } else if (c->geoms[i]->type == TRIANGLETYPE) { /* 3D raycast along Z is 2D point in polygon */ LWTRIANGLE *tri = (LWTRIANGLE *)c->geoms[i]; int t = ptarray_contains_point(tri->points, (POINT2D *)&pt); if (t == LW_INSIDE) is_inside = !is_inside; else if (t == LW_BOUNDARY) { is_boundary = LW_TRUE; break; } } } lwcollection_free(c); lwgeom_free(solid_copy); lwgeom_free(g_copy); if (is_boundary) { /* randomly skew a bit and restart*/ double cx = lwrandom_uniform() - 0.5; double cy = lwrandom_uniform() - 0.5; AFFINE aff = {1, 0, cx, 0, 1, cy, 0, 0, 1, 0, 0, 0}; solid_copy = lwgeom_clone_deep(solid); lwgeom_affine(solid_copy, &aff); g_copy = lwgeom_clone_deep(g); lwgeom_affine(g_copy, &aff); continue; } return is_inside; } } } /** Function handling 3d min distance calculations and dwithin calculations. The difference is just the tolerance. */ double lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) { assert(tolerance >= 0); if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) { lwnotice( "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); return lwgeom_mindistance2d_tolerance(lw1, lw2, tolerance); } DISTPTS3D thedl; thedl.mode = DIST_MIN; thedl.distance = DBL_MAX; thedl.tolerance = tolerance; if (lw_dist3d_recursive(lw1, lw2, &thedl)) { if (thedl.distance <= tolerance) return thedl.distance; if (lwgeom_solid_contains_lwgeom(lw1, lw2) || lwgeom_solid_contains_lwgeom(lw2, lw1)) return 0; return thedl.distance; } /* should never get here. all cases ought to be error handled earlier */ lwerror("Some unspecified error."); return DBL_MAX; } /*------------------------------------------------------------------------------------------------------------ End of Initializing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Preprocessing functions Functions preparing geometries for distance-calculations --------------------------------------------------------------------------------------------------------------*/ /** This is a recursive function delivering every possible combination of subgeometries */ int lw_dist3d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl) { int i, j; int n1 = 1; int n2 = 1; LWGEOM *g1 = NULL; LWGEOM *g2 = NULL; LWCOLLECTION *c1 = NULL; LWCOLLECTION *c2 = NULL; LWDEBUGF(2, "lw_dist3d_recursive is called with type1=%d, type2=%d", lwg1->type, lwg2->type); if (lwgeom_is_collection(lwg1)) { LWDEBUG(3, "First geometry is collection"); c1 = lwgeom_as_lwcollection(lwg1); n1 = c1->ngeoms; } if (lwgeom_is_collection(lwg2)) { LWDEBUG(3, "Second geometry is collection"); c2 = lwgeom_as_lwcollection(lwg2); n2 = c2->ngeoms; } for (i = 0; i < n1; i++) { if (lwgeom_is_collection(lwg1)) g1 = c1->geoms[i]; else g1 = (LWGEOM *)lwg1; if (lwgeom_is_empty(g1)) return LW_TRUE; if (lwgeom_is_collection(g1)) { LWDEBUG(3, "Found collection inside first geometry collection, recursing"); if (!lw_dist3d_recursive(g1, lwg2, dl)) return LW_FALSE; continue; } for (j = 0; j < n2; j++) { if (lwgeom_is_collection(lwg2)) g2 = c2->geoms[j]; else g2 = (LWGEOM *)lwg2; if (lwgeom_is_collection(g2)) { LWDEBUG(3, "Found collection inside second geometry collection, recursing"); if (!lw_dist3d_recursive(g1, g2, dl)) return LW_FALSE; continue; } /*If one of geometries is empty, return. True here only means continue searching. False would * have stopped the process*/ if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2)) return LW_TRUE; if (!lw_dist3d_distribute_bruteforce(g1, g2, dl)) return LW_FALSE; if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ } } return LW_TRUE; } /** This function distributes the brute-force for 3D so far the only type, tasks depending on type */ int lw_dist3d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl) { int t1 = lwg1->type; int t2 = lwg2->type; LWDEBUGF(2, "lw_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", lwg1->type, lwg2->type); if (t1 == POINTTYPE) { if (t2 == POINTTYPE) { dl->twisted = 1; return lw_dist3d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl); } else if (t2 == LINETYPE) { dl->twisted = 1; return lw_dist3d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl); } else if (t2 == POLYGONTYPE) { dl->twisted = 1; return lw_dist3d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl); } else if (t2 == TRIANGLETYPE) { dl->twisted = 1; return lw_dist3d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl); } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } else if (t1 == LINETYPE) { if (t2 == POINTTYPE) { dl->twisted = -1; return lw_dist3d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl); } else if (t2 == LINETYPE) { dl->twisted = 1; return lw_dist3d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl); } else if (t2 == POLYGONTYPE) { dl->twisted = 1; return lw_dist3d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl); } else if (t2 == TRIANGLETYPE) { dl->twisted = 1; return lw_dist3d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl); } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } else if (t1 == POLYGONTYPE) { if (t2 == POLYGONTYPE) { dl->twisted = 1; return lw_dist3d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl); } else if (t2 == POINTTYPE) { dl->twisted = -1; return lw_dist3d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl); } else if (t2 == LINETYPE) { dl->twisted = -1; return lw_dist3d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl); } else if (t2 == TRIANGLETYPE) { dl->twisted = 1; return lw_dist3d_poly_tri((LWPOLY *)lwg1, (LWTRIANGLE *)lwg2, dl); } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } else if (t1 == TRIANGLETYPE) { if (t2 == POLYGONTYPE) { dl->twisted = -1; return lw_dist3d_poly_tri((LWPOLY *)lwg2, (LWTRIANGLE *)lwg1, dl); } else if (t2 == POINTTYPE) { dl->twisted = -1; return lw_dist3d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl); } else if (t2 == LINETYPE) { dl->twisted = -1; return lw_dist3d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl); } else if (t2 == TRIANGLETYPE) { dl->twisted = 1; return lw_dist3d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl); } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } else { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1)); return LW_FALSE; } } /*------------------------------------------------------------------------------------------------------------ End of Preprocessing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Brute force functions So far the only way to do 3D-calculations --------------------------------------------------------------------------------------------------------------*/ /** point to point calculation */ int lw_dist3d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS3D *dl) { POINT3DZ p1; POINT3DZ p2; LWDEBUG(2, "lw_dist3d_point_point is called"); getPoint3dz_p(point1->point, 0, &p1); getPoint3dz_p(point2->point, 0, &p2); return lw_dist3d_pt_pt(&p1, &p2, dl); } /** point to line calculation */ int lw_dist3d_point_line(LWPOINT *point, LWLINE *line, DISTPTS3D *dl) { POINT3DZ p; POINTARRAY *pa = line->points; LWDEBUG(2, "lw_dist3d_point_line is called"); getPoint3dz_p(point->point, 0, &p); return lw_dist3d_pt_ptarray(&p, pa, dl); } /** Computes point to polygon distance For mindistance that means: 1) find the plane of the polygon 2) projecting the point to the plane of the polygon 3) finding if that projected point is inside the polygon, if so the distance is measured to that projected point 4) if not in polygon above, check the distance against the boundary of the polygon for max distance it is always point against boundary */ int lw_dist3d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS3D *dl) { POINT3DZ p, projp; /*projp is "point projected on plane"*/ PLANE3D plane; LWDEBUG(2, "lw_dist3d_point_poly is called"); getPoint3dz_p(point->point, 0, &p); /* If we are looking for max distance, longestline or dfullywithin */ if (dl->mode == DIST_MAX) return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl); /* Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boudary */ if (!define_plane(poly->rings[0], &plane)) { /* Polygon does not define a plane: Return distance point -> line */ return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl); } /* Get our point projected on the plane of the polygon */ project_point_on_plane(&p, &plane, &projp); return lw_dist3d_pt_poly(&p, poly, &plane, &projp, dl); } /* point to triangle calculation */ int lw_dist3d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS3D *dl) { POINT3DZ p, projp; /*projp is "point projected on plane"*/ PLANE3D plane; getPoint3dz_p(point->point, 0, &p); /* If we are looking for max distance, longestline or dfullywithin */ if (dl->mode == DIST_MAX) return lw_dist3d_pt_ptarray(&p, tri->points, dl); /* If triangle does not define a plane, treat it as a line */ if (!define_plane(tri->points, &plane)) return lw_dist3d_pt_ptarray(&p, tri->points, dl); /* Get our point projected on the plane of triangle */ project_point_on_plane(&p, &plane, &projp); return lw_dist3d_pt_tri(&p, tri, &plane, &projp, dl); } /** line to line calculation */ int lw_dist3d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS3D *dl) { POINTARRAY *pa1 = line1->points; POINTARRAY *pa2 = line2->points; LWDEBUG(2, "lw_dist3d_line_line is called"); return lw_dist3d_ptarray_ptarray(pa1, pa2, dl); } /** line to polygon calculation */ int lw_dist3d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS3D *dl) { PLANE3D plane; LWDEBUG(2, "lw_dist3d_line_poly is called"); if (dl->mode == DIST_MAX) return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl); /* if polygon does not define a plane: Return distance line to line */ if (!define_plane(poly->rings[0], &plane)) return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl); return lw_dist3d_ptarray_poly(line->points, poly, &plane, dl); } /** line to triangle calculation */ int lw_dist3d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS3D *dl) { PLANE3D plane; if (dl->mode == DIST_MAX) return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl); /* if triangle does not define a plane: Return distance line to line */ if (!define_plane(tri->points, &plane)) return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl); return lw_dist3d_ptarray_tri(line->points, tri, &plane, dl); } /** polygon to polygon calculation */ int lw_dist3d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS3D *dl) { PLANE3D plane1, plane2; int planedef1, planedef2; LWDEBUG(2, "lw_dist3d_poly_poly is called"); if (dl->mode == DIST_MAX) return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); planedef1 = define_plane(poly1->rings[0], &plane1); planedef2 = define_plane(poly2->rings[0], &plane2); if (!planedef1 || !planedef2) { /* Neither polygon define a plane: Return distance line to line */ if (!planedef1 && !planedef2) return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); /* Only poly2 defines a plane: Return distance from line (poly1) to poly2 */ else if (!planedef1) return lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl); /* Only poly1 defines a plane: Return distance from line (poly2) to poly1 */ else return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl); } /* What we do here is to compare the boundary of one polygon with the other polygon and then take the second boundary comparing with the first polygon */ dl->twisted = 1; if (!lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl)) return LW_FALSE; if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ return LW_TRUE; dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the right order of points in shortest line. */ return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl); } /** polygon to triangle calculation */ int lw_dist3d_poly_tri(LWPOLY *poly, LWTRIANGLE *tri, DISTPTS3D *dl) { PLANE3D plane1, plane2; int planedef1, planedef2; if (dl->mode == DIST_MAX) return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl); planedef1 = define_plane(poly->rings[0], &plane1); planedef2 = define_plane(tri->points, &plane2); if (!planedef1 || !planedef2) { /* Neither define a plane: Return distance line to line */ if (!planedef1 && !planedef2) return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl); /* Only tri defines a plane: Return distance from line (poly) to tri */ else if (!planedef1) return lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl); /* Only poly defines a plane: Return distance from line (tri) to poly */ else return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl); } /* What we do here is to compare the boundary of one polygon with the other polygon and then take the second boundary comparing with the first polygon */ dl->twisted = 1; if (!lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl)) return LW_FALSE; if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ return LW_TRUE; dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the right order of points in shortest line. */ return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl); } /** triangle to triangle calculation */ int lw_dist3d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS3D *dl) { PLANE3D plane1, plane2; int planedef1, planedef2; if (dl->mode == DIST_MAX) return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl); planedef1 = define_plane(tri1->points, &plane1); planedef2 = define_plane(tri2->points, &plane2); if (!planedef1 || !planedef2) { /* Neither define a plane: Return distance line to line */ if (!planedef1 && !planedef2) return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl); /* Only tri defines a plane: Return distance from line (tri1) to tri2 */ else if (!planedef1) return lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl); /* Only poly defines a plane: Return distance from line (tri2) to tri1 */ else return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl); } /* What we do here is to compare the boundary of one polygon with the other polygon and then take the second boundary comparing with the first polygon */ dl->twisted = 1; if (!lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl)) return LW_FALSE; if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ return LW_TRUE; dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the right order of points in shortest line. */ return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl); } /** * search all the segments of pointarray to see which one is closest to p * Returns distance between point and pointarray */ int lw_dist3d_pt_ptarray(POINT3DZ *p, POINTARRAY *pa, DISTPTS3D *dl) { uint32_t t; POINT3DZ start, end; int twist = dl->twisted; if (!pa) return LW_FALSE; getPoint3dz_p(pa, 0, &start); for (t = 1; t < pa->npoints; t++) { dl->twisted = twist; getPoint3dz_p(pa, t, &end); if (!lw_dist3d_pt_seg(p, &start, &end, dl)) return LW_FALSE; if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ start = end; } return LW_TRUE; } /** If searching for min distance, this one finds the closest point on segment A-B from p. if searching for max distance it just sends p-A and p-B to pt-pt calculation */ int lw_dist3d_pt_seg(POINT3DZ *p, POINT3DZ *A, POINT3DZ *B, DISTPTS3D *dl) { POINT3DZ c; double r; /*if start==end, then use pt distance */ if ((A->x == B->x) && (A->y == B->y) && (A->z == B->z)) { return lw_dist3d_pt_pt(p, A, dl); } r = ((p->x - A->x) * (B->x - A->x) + (p->y - A->y) * (B->y - A->y) + (p->z - A->z) * (B->z - A->z)) / ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y) + (B->z - A->z) * (B->z - A->z)); /*This is for finding the 3Dmaxdistance. the maxdistance have to be between two vertexes, compared to mindistance which can be between two vertexes vertex.*/ if (dl->mode == DIST_MAX) { if (r >= 0.5) return lw_dist3d_pt_pt(p, A, dl); if (r < 0.5) return lw_dist3d_pt_pt(p, B, dl); } if (r < 0) /*If the first vertex A is closest to the point p*/ return lw_dist3d_pt_pt(p, A, dl); if (r > 1) /*If the second vertex B is closest to the point p*/ return lw_dist3d_pt_pt(p, B, dl); /*else if the point p is closer to some point between a and b then we find that point and send it to lw_dist3d_pt_pt*/ c.x = A->x + r * (B->x - A->x); c.y = A->y + r * (B->y - A->y); c.z = A->z + r * (B->z - A->z); return lw_dist3d_pt_pt(p, &c, dl); } double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2) { double dx = p2->x - p1->x; double dy = p2->y - p1->y; double dz = p2->z - p1->z; return sqrt(dx * dx + dy * dy + dz * dz); } /** Compares incoming points and stores the points closest to each other or most far away from each other depending on dl->mode (max or min) */ int lw_dist3d_pt_pt(POINT3DZ *thep1, POINT3DZ *thep2, DISTPTS3D *dl) { double dx = thep2->x - thep1->x; double dy = thep2->y - thep1->y; double dz = thep2->z - thep1->z; double dist = sqrt(dx * dx + dy * dy + dz * dz); LWDEBUGF(2, "lw_dist3d_pt_pt called (with points: p1.x=%f, p1.y=%f,p1.z=%f,p2.x=%f, p2.y=%f,p2.z=%f)", thep1->x, thep1->y, thep1->z, thep2->x, thep2->y, thep2->z); if (((dl->distance - dist) * (dl->mode)) > 0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/ { dl->distance = dist; if (dl->twisted > 0) /*To get the points in right order. twisted is updated between 1 and (-1) every time the order is changed earlier in the chain*/ { dl->p1 = *thep1; dl->p2 = *thep2; } else { dl->p1 = *thep2; dl->p2 = *thep1; } } return LW_TRUE; } /** Finds all combinations of segments between two pointarrays */ int lw_dist3d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS3D *dl) { uint32_t t, u; POINT3DZ start, end; POINT3DZ start2, end2; int twist = dl->twisted; LWDEBUGF(2, "lw_dist3d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints); if (dl->mode == DIST_MAX) /*If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have to be between two vertexes*/ { for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */ { getPoint3dz_p(l1, t, &start); for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */ { getPoint3dz_p(l2, u, &start2); lw_dist3d_pt_pt(&start, &start2, dl); } } } else { getPoint3dz_p(l1, 0, &start); for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */ { getPoint3dz_p(l1, t, &end); getPoint3dz_p(l2, 0, &start2); for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */ { getPoint3dz_p(l2, u, &end2); dl->twisted = twist; lw_dist3d_seg_seg(&start, &end, &start2, &end2, dl); LWDEBUGF( 4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n", t, u, dl->distance); LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ start2 = end2; } start = end; } } return LW_TRUE; } /** Finds the two closest points on two linesegments */ int lw_dist3d_seg_seg(POINT3DZ *s1p1, POINT3DZ *s1p2, POINT3DZ *s2p1, POINT3DZ *s2p2, DISTPTS3D *dl) { VECTOR3D v1, v2, vl; double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line between the two lines is perpendicular to both lines*/ POINT3DZ p1, p2; double a, b, c, d, e, D; /*s1p1 and s1p2 are the same point */ if ((s1p1->x == s1p2->x) && (s1p1->y == s1p2->y) && (s1p1->z == s1p2->z)) { return lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl); } /*s2p1 and s2p2 are the same point */ if ((s2p1->x == s2p2->x) && (s2p1->y == s2p2->y) && (s2p1->z == s2p2->z)) { dl->twisted = ((dl->twisted) * (-1)); return lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl); } /* Here we use algorithm from softsurfer.com that can be found here http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm */ if (!get_3dvector_from_points(s1p1, s1p2, &v1)) return LW_FALSE; if (!get_3dvector_from_points(s2p1, s2p2, &v2)) return LW_FALSE; if (!get_3dvector_from_points(s2p1, s1p1, &vl)) return LW_FALSE; a = DOT(v1, v1); b = DOT(v1, v2); c = DOT(v2, v2); d = DOT(v1, vl); e = DOT(v2, vl); D = a * c - b * b; if (D < 0.000000001) { /* the lines are almost parallel*/ s1k = 0.0; /*If the lines are parallel we try by using the startpoint of first segment. If that gives a projected point on the second line outside segment 2 it wil be found that s2k is >1 or <0.*/ if (b > c) /* use the largest denominator*/ s2k = d / b; else s2k = e / c; } else { s1k = (b * e - c * d) / D; s2k = (a * e - b * d) / D; } /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the * combinations with start and end points will be tested*/ if (s1k < 0.0 || s1k > 1.0 || s2k < 0.0 || s2k > 1.0) { if (s1k < 0.0) { if (!lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl)) return LW_FALSE; } if (s1k > 1.0) { if (!lw_dist3d_pt_seg(s1p2, s2p1, s2p2, dl)) return LW_FALSE; } if (s2k < 0.0) { dl->twisted = ((dl->twisted) * (-1)); if (!lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl)) return LW_FALSE; } if (s2k > 1.0) { dl->twisted = ((dl->twisted) * (-1)); if (!lw_dist3d_pt_seg(s2p2, s1p1, s1p2, dl)) return LW_FALSE; } } else { /*Find the closest point on the edges of both segments*/ p1.x = s1p1->x + s1k * (s1p2->x - s1p1->x); p1.y = s1p1->y + s1k * (s1p2->y - s1p1->y); p1.z = s1p1->z + s1k * (s1p2->z - s1p1->z); p2.x = s2p1->x + s2k * (s2p2->x - s2p1->x); p2.y = s2p1->y + s2k * (s2p2->y - s2p1->y); p2.z = s2p1->z + s2k * (s2p2->z - s2p1->z); if (!lw_dist3d_pt_pt(&p1, &p2, dl)) /* Send the closest points to point-point calculation*/ { return LW_FALSE; } } return LW_TRUE; } /** Checking if the point projected on the plane of the polygon actually is inside that polygon. If so the mindistance is between that projected point and our original point. If not we check from original point to the boundary. If the projected point is inside a hole of the polygon we check the distance to the boundary of that hole. */ int lw_dist3d_pt_poly(POINT3DZ *p, LWPOLY *poly, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl) { uint32_t i; if (pt_in_ring_3d(projp, poly->rings[0], plane)) { for (i = 1; i < poly->nrings; i++) { /* Inside a hole. Distance = pt -> ring */ if (pt_in_ring_3d(projp, poly->rings[i], plane)) return lw_dist3d_pt_ptarray(p, poly->rings[i], dl); } /* if the projected point is inside the polygon the shortest distance is between that point and the * input point */ return lw_dist3d_pt_pt(p, projp, dl); } else /* if the projected point is outside the polygon we search for the closest distance against the boundary * instead */ return lw_dist3d_pt_ptarray(p, poly->rings[0], dl); return LW_TRUE; } int lw_dist3d_pt_tri(POINT3DZ *p, LWTRIANGLE *tri, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl) { if (pt_in_ring_3d(projp, tri->points, plane)) /* if the projected point is inside the polygon the shortest distance is between that point and the * input point */ return lw_dist3d_pt_pt(p, projp, dl); else /* if the projected point is outside the polygon we search for the closest distance against the boundary * instead */ return lw_dist3d_pt_ptarray(p, tri->points, dl); return LW_TRUE; } /** Computes pointarray to polygon distance */ int lw_dist3d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly, PLANE3D *plane, DISTPTS3D *dl) { uint32_t i, j, k; double f, s1, s2; VECTOR3D projp1_projp2; POINT3DZ p1, p2, projp1, projp2, intersectionp; getPoint3dz_p(pa, 0, &p1); /* the sign of s1 tells us on which side of the plane the point is. */ s1 = project_point_on_plane(&p1, plane, &projp1); lw_dist3d_pt_poly(&p1, poly, plane, &projp1, dl); if ((s1 == 0.0) && (dl->distance < dl->tolerance)) return LW_TRUE; for (i = 1; i < pa->npoints; i++) { int intersects; getPoint3dz_p(pa, i, &p2); s2 = project_point_on_plane(&p2, plane, &projp2); lw_dist3d_pt_poly(&p2, poly, plane, &projp2, dl); if ((s2 == 0.0) && (dl->distance < dl->tolerance)) return LW_TRUE; /* If s1 and s2 has different signs that means they are on different sides of the plane of the polygon. * That means that the edge between the points crosses the plane and might intersect with the polygon */ if ((s1 * s2) < 0) { /* The size of s1 and s2 is the distance from the point to the plane. */ f = fabs(s1) / (fabs(s1) + fabs(s2)); get_3dvector_from_points(&projp1, &projp2, &projp1_projp2); /* Get the point where the line segment crosses the plane */ intersectionp.x = projp1.x + f * projp1_projp2.x; intersectionp.y = projp1.y + f * projp1_projp2.y; intersectionp.z = projp1.z + f * projp1_projp2.z; /* We set intersects to true until the opposite is proved */ intersects = LW_TRUE; if (pt_in_ring_3d(&intersectionp, poly->rings[0], plane)) /*Inside outer ring*/ { for (k = 1; k < poly->nrings; k++) { /* Inside a hole, so no intersection with the polygon*/ if (pt_in_ring_3d(&intersectionp, poly->rings[k], plane)) { intersects = LW_FALSE; break; } } if (intersects) { dl->distance = 0.0; dl->p1.x = intersectionp.x; dl->p1.y = intersectionp.y; dl->p1.z = intersectionp.z; dl->p2.x = intersectionp.x; dl->p2.y = intersectionp.y; dl->p2.z = intersectionp.z; return LW_TRUE; } } } projp1 = projp2; s1 = s2; p1 = p2; } /* check our pointarray against boundary and inner boundaries of the polygon */ for (j = 0; j < poly->nrings; j++) lw_dist3d_ptarray_ptarray(pa, poly->rings[j], dl); return LW_TRUE; } /** Computes pointarray to triangle distance */ int lw_dist3d_ptarray_tri(POINTARRAY *pa, LWTRIANGLE *tri, PLANE3D *plane, DISTPTS3D *dl) { uint32_t i; double f, s1, s2; VECTOR3D projp1_projp2; POINT3DZ p1, p2, projp1, projp2, intersectionp; getPoint3dz_p(pa, 0, &p1); /* the sign of s1 tells us on which side of the plane the point is. */ s1 = project_point_on_plane(&p1, plane, &projp1); lw_dist3d_pt_tri(&p1, tri, plane, &projp1, dl); if ((s1 == 0.0) && (dl->distance < dl->tolerance)) return LW_TRUE; for (i = 1; i < pa->npoints; i++) { int intersects; getPoint3dz_p(pa, i, &p2); s2 = project_point_on_plane(&p2, plane, &projp2); lw_dist3d_pt_tri(&p2, tri, plane, &projp2, dl); if ((s2 == 0.0) && (dl->distance < dl->tolerance)) return LW_TRUE; /* If s1 and s2 has different signs that means they are on different sides of the plane of the triangle. * That means that the edge between the points crosses the plane and might intersect with the triangle */ if ((s1 * s2) < 0) { /* The size of s1 and s2 is the distance from the point to the plane. */ f = fabs(s1) / (fabs(s1) + fabs(s2)); get_3dvector_from_points(&projp1, &projp2, &projp1_projp2); /* Get the point where the line segment crosses the plane */ intersectionp.x = projp1.x + f * projp1_projp2.x; intersectionp.y = projp1.y + f * projp1_projp2.y; intersectionp.z = projp1.z + f * projp1_projp2.z; /* We set intersects to true until the opposite is proved */ intersects = LW_TRUE; if (pt_in_ring_3d(&intersectionp, tri->points, plane)) /*Inside outer ring*/ { if (intersects) { dl->distance = 0.0; dl->p1.x = intersectionp.x; dl->p1.y = intersectionp.y; dl->p1.z = intersectionp.z; dl->p2.x = intersectionp.x; dl->p2.y = intersectionp.y; dl->p2.z = intersectionp.z; return LW_TRUE; } } } projp1 = projp2; s1 = s2; p1 = p2; } /* check our pointarray against triangle contour */ lw_dist3d_ptarray_ptarray(pa, tri->points, dl); return LW_TRUE; } /* Here we define the plane of a polygon (boundary pointarray of a polygon) * the plane is stored as a point in plane (plane.pop) and a normal vector (plane.pv) * * We are calculating the average point and using it to break the polygon into * POL_BREAKS (3) parts. Then we calculate the normal of those 3 vectors and * use its average to define the normal of the plane.This is done to minimize * the error introduced by FP precision * We use [3] breaks to contemplate the special case of 3d triangles */ int define_plane(POINTARRAY *pa, PLANE3D *pl) { const uint32_t POL_BREAKS = 3; assert(pa); assert(pa->npoints > 3); if (!pa) return LW_FALSE; uint32_t unique_points = pa->npoints - 1; uint32_t i; if (pa->npoints < 3) return LW_FALSE; /* Calculate the average point */ pl->pop.x = 0.0; pl->pop.y = 0.0; pl->pop.z = 0.0; for (i = 0; i < unique_points; i++) { POINT3DZ p; getPoint3dz_p(pa, i, &p); pl->pop.x += p.x; pl->pop.y += p.y; pl->pop.z += p.z; } pl->pop.x /= unique_points; pl->pop.y /= unique_points; pl->pop.z /= unique_points; pl->pv.x = 0.0; pl->pv.y = 0.0; pl->pv.z = 0.0; for (i = 0; i < POL_BREAKS; i++) { POINT3DZ point1, point2; VECTOR3D v1, v2, vp; uint32_t n1, n2; n1 = i * unique_points / POL_BREAKS; n2 = n1 + unique_points / POL_BREAKS; /* May overflow back to the first / last point */ if (n1 == n2) continue; getPoint3dz_p(pa, n1, &point1); if (!get_3dvector_from_points(&pl->pop, &point1, &v1)) continue; getPoint3dz_p(pa, n2, &point2); if (!get_3dvector_from_points(&pl->pop, &point2, &v2)) continue; if (get_3dcross_product(&v1, &v2, &vp)) { /* If the cross product isn't zero these 3 points aren't collinear * We divide by its lengthsq to normalize the additions */ double vl = vp.x * vp.x + vp.y * vp.y + vp.z * vp.z; pl->pv.x += vp.x / vl; pl->pv.y += vp.y / vl; pl->pv.z += vp.z / vl; } } return (!FP_IS_ZERO(pl->pv.x) || !FP_IS_ZERO(pl->pv.y) || !FP_IS_ZERO(pl->pv.z)); } /** Finds a point on a plane from where the original point is perpendicular to the plane */ double project_point_on_plane(POINT3DZ *p, PLANE3D *pl, POINT3DZ *p0) { /*In our plane definition we have a point on the plane and a normal vector (pl.pv), perpendicular to the plane this vector will be parallel to the line between our inputted point above the plane and the point we are searching for on the plane. So, we already have a direction from p to find p0, but we don't know the distance. */ VECTOR3D v1; double f; if (!get_3dvector_from_points(&(pl->pop), p, &v1)) return 0.0; f = DOT(pl->pv, v1); if (FP_IS_ZERO(f)) { /* Point is in the plane */ *p0 = *p; return 0; } f = -f / DOT(pl->pv, pl->pv); p0->x = p->x + pl->pv.x * f; p0->y = p->y + pl->pv.y * f; p0->z = p->z + pl->pv.z * f; return f; } /** * pt_in_ring_3d(): crossing number test for a point in a polygon * input: p = a point, * pa = vertex points of a ring V[n+1] with V[n]=V[0] * plane=the plane that the vertex points are lying on * returns: 0 = outside, 1 = inside * * Our polygons have first and last point the same, * * The difference in 3D variant is that we exclude the dimension that faces the plane least. * That is the dimension with the highest number in pv */ int pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane) { uint32_t cn = 0; /* the crossing number counter */ uint32_t i; POINT3DZ v1, v2; POINT3DZ first, last; getPoint3dz_p(ring, 0, &first); getPoint3dz_p(ring, ring->npoints - 1, &last); if (memcmp(&first, &last, sizeof(POINT3DZ))) { lwerror("pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)", first.x, first.y, first.z, last.x, last.y, last.z); return LW_FALSE; } LWDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z); /* printPA(ring); */ /* loop through all edges of the polygon */ getPoint3dz_p(ring, 0, &v1); if (fabs(plane->pv.z) >= fabs(plane->pv.x) && fabs(plane->pv.z) >= fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x and y vector we project the ring to the xy-plane*/ { for (i = 0; i < ring->npoints - 1; i++) { double vt; getPoint3dz_p(ring, i + 1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.y <= p->y) && (v2.y > p->y)) /* a downward crossing */ || ((v1.y > p->y) && (v2.y <= p->y))) { vt = (double)(p->y - v1.y) / (v2.y - v1.y); /* P.x x < v1.x + vt * (v2.x - v1.x)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } else if (fabs(plane->pv.y) >= fabs(plane->pv.x) && fabs(plane->pv.y) >= fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger than x and z vector we project the ring to the xz-plane*/ { for (i = 0; i < ring->npoints - 1; i++) { double vt; getPoint3dz_p(ring, i + 1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z))) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* P.x x < v1.x + vt * (v2.x - v1.x)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } else /*Hopefully we only have the cases where x part of the normal vector is largest left*/ { for (i = 0; i < ring->npoints - 1; i++) { double vt; getPoint3dz_p(ring, i + 1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z))) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* P.x y < v1.y + vt * (v2.y - v1.y)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } LWDEBUGF(3, "pt_in_ring_3d returning %d", cn & 1); return (cn & 1); /* 0 if even (out), and 1 if odd (in) */ } /*------------------------------------------------------------------------------------------------------------ End of Brute force functions --------------------------------------------------------------------------------------------------------------*/ lwgeom/src/liblwgeom/lwcompound.c0000644000176200001440000001462713773172540016672 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" int lwcompound_is_closed(const LWCOMPOUND *compound) { size_t size; int npoints=0; if ( lwgeom_has_z((LWGEOM*)compound) ) { size = sizeof(POINT3D); } else { size = sizeof(POINT2D); } if ( compound->geoms[compound->ngeoms - 1]->type == CIRCSTRINGTYPE ) { npoints = ((LWCIRCSTRING *)compound->geoms[compound->ngeoms - 1])->points->npoints; } else if (compound->geoms[compound->ngeoms - 1]->type == LINETYPE) { npoints = ((LWLINE *)compound->geoms[compound->ngeoms - 1])->points->npoints; } if ( memcmp(getPoint_internal( (POINTARRAY *)compound->geoms[0]->data, 0), getPoint_internal( (POINTARRAY *)compound->geoms[compound->ngeoms - 1]->data, npoints - 1), size) ) { return LW_FALSE; } return LW_TRUE; } double lwcompound_length(const LWCOMPOUND *comp) { return lwcompound_length_2d(comp); } double lwcompound_length_2d(const LWCOMPOUND *comp) { uint32_t i; double length = 0.0; if ( lwgeom_is_empty((LWGEOM*)comp) ) return 0.0; for (i = 0; i < comp->ngeoms; i++) { length += lwgeom_length_2d(comp->geoms[i]); } return length; } int lwcompound_add_lwgeom(LWCOMPOUND *comp, LWGEOM *geom) { LWCOLLECTION *col = (LWCOLLECTION*)comp; /* Empty things can't continuously join up with other things */ if ( lwgeom_is_empty(geom) ) { LWDEBUG(4, "Got an empty component for a compound curve!"); return LW_FAILURE; } if( col->ngeoms > 0 ) { POINT4D last, first; /* First point of the component we are adding */ LWLINE *newline = (LWLINE*)geom; /* Last point of the previous component */ LWLINE *prevline = (LWLINE*)(col->geoms[col->ngeoms-1]); getPoint4d_p(newline->points, 0, &first); getPoint4d_p(prevline->points, prevline->points->npoints-1, &last); if ( !(FP_EQUALS(first.x,last.x) && FP_EQUALS(first.y,last.y)) ) { LWDEBUG(4, "Components don't join up end-to-end!"); LWDEBUGF(4, "first pt (%g %g %g %g) last pt (%g %g %g %g)", first.x, first.y, first.z, first.m, last.x, last.y, last.z, last.m); return LW_FAILURE; } } col = lwcollection_add_lwgeom(col, geom); return LW_SUCCESS; } LWCOMPOUND * lwcompound_construct_empty(int32_t srid, char hasz, char hasm) { LWCOMPOUND *ret = (LWCOMPOUND*)lwcollection_construct_empty(COMPOUNDTYPE, srid, hasz, hasm); return ret; } int lwgeom_contains_point(const LWGEOM *geom, const POINT2D *pt) { switch( geom->type ) { case LINETYPE: return ptarray_contains_point(((LWLINE*)geom)->points, pt); case CIRCSTRINGTYPE: return ptarrayarc_contains_point(((LWCIRCSTRING*)geom)->points, pt); case COMPOUNDTYPE: return lwcompound_contains_point((LWCOMPOUND*)geom, pt); } lwerror("lwgeom_contains_point failed"); return LW_FAILURE; } int lwcompound_contains_point(const LWCOMPOUND *comp, const POINT2D *pt) { uint32_t i; LWLINE *lwline; LWCIRCSTRING *lwcirc; int wn = 0; int winding_number = 0; int result; for ( i = 0; i < comp->ngeoms; i++ ) { LWGEOM *lwgeom = comp->geoms[i]; if ( lwgeom->type == LINETYPE ) { lwline = lwgeom_as_lwline(lwgeom); if ( comp->ngeoms == 1 ) { return ptarray_contains_point(lwline->points, pt); } else { /* Don't check closure while doing p-i-p test */ result = ptarray_contains_point_partial(lwline->points, pt, LW_FALSE, &winding_number); } } else { lwcirc = lwgeom_as_lwcircstring(lwgeom); if ( ! lwcirc ) { lwerror("Unexpected component of type %s in compound curve", lwtype_name(lwgeom->type)); return 0; } if ( comp->ngeoms == 1 ) { return ptarrayarc_contains_point(lwcirc->points, pt); } else { /* Don't check closure while doing p-i-p test */ result = ptarrayarc_contains_point_partial(lwcirc->points, pt, LW_FALSE, &winding_number); } } /* Propogate boundary condition */ if ( result == LW_BOUNDARY ) return LW_BOUNDARY; wn += winding_number; } /* Outside */ if (wn == 0) return LW_OUTSIDE; /* Inside */ return LW_INSIDE; } LWCOMPOUND * lwcompound_construct_from_lwline(const LWLINE *lwline) { LWCOMPOUND* ogeom = lwcompound_construct_empty(lwline->srid, FLAGS_GET_Z(lwline->flags), FLAGS_GET_M(lwline->flags)); lwcompound_add_lwgeom(ogeom, lwgeom_clone((LWGEOM*)lwline)); /* ogeom->bbox = lwline->bbox; */ return ogeom; } LWPOINT* lwcompound_get_lwpoint(const LWCOMPOUND *lwcmp, uint32_t where) { uint32_t i; uint32_t count = 0; uint32_t npoints = 0; if ( lwgeom_is_empty((LWGEOM*)lwcmp) ) return NULL; npoints = lwgeom_count_vertices((LWGEOM*)lwcmp); if ( where >= npoints ) { lwerror("%s: index %d is not in range of number of vertices (%d) in input", __func__, where, npoints); return NULL; } for ( i = 0; i < lwcmp->ngeoms; i++ ) { LWGEOM* part = lwcmp->geoms[i]; uint32_t npoints_part = lwgeom_count_vertices(part); if ( where >= count && where < count + npoints_part ) { return lwline_get_lwpoint((LWLINE*)part, where - count); } else { count += npoints_part; } } return NULL; } LWPOINT * lwcompound_get_startpoint(const LWCOMPOUND *lwcmp) { return lwcompound_get_lwpoint(lwcmp, 0); } LWPOINT * lwcompound_get_endpoint(const LWCOMPOUND *lwcmp) { LWLINE *lwline; if ( lwcmp->ngeoms < 1 ) { return NULL; } lwline = (LWLINE*)(lwcmp->geoms[lwcmp->ngeoms-1]); if ( (!lwline) || (!lwline->points) || (lwline->points->npoints < 1) ) { return NULL; } return lwline_get_lwpoint(lwline, lwline->points->npoints-1); } lwgeom/src/liblwgeom/lwin_wkt.h0000644000176200001440000000606413773172540016342 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2010-2015 Paul Ramsey * Copyright (C) 2011 Sandro Santilli * **********************************************************************/ #include "liblwgeom_internal.h" /* * Coordinate object to hold information about last coordinate temporarily. * We need to know how many dimensions there are at any given time. */ typedef struct { lwflags_t flags; double x; double y; double z; double m; } POINT; /* * Global that holds the final output geometry for the WKT parser. */ extern LWGEOM_PARSER_RESULT global_parser_result; extern const char *parser_error_messages[]; /* * Prototypes for the lexer */ extern void wkt_lexer_init(char *str); extern void wkt_lexer_close(void); extern int wkt_yylex_destroy(void); /* * Functions called from within the bison parser to construct geometries. */ int32_t wkt_lexer_read_srid(char *str); POINT wkt_parser_coord_2(double c1, double c2); POINT wkt_parser_coord_3(double c1, double c2, double c3); POINT wkt_parser_coord_4(double c1, double c2, double c3, double c4); POINTARRAY* wkt_parser_ptarray_add_coord(POINTARRAY *pa, POINT p); POINTARRAY* wkt_parser_ptarray_new(POINT p); LWGEOM* wkt_parser_point_new(POINTARRAY *pa, char *dimensionality); LWGEOM* wkt_parser_linestring_new(POINTARRAY *pa, char *dimensionality); LWGEOM* wkt_parser_circularstring_new(POINTARRAY *pa, char *dimensionality); LWGEOM* wkt_parser_triangle_new(POINTARRAY *pa, char *dimensionality); LWGEOM* wkt_parser_polygon_new(POINTARRAY *pa, char dimcheck); LWGEOM* wkt_parser_polygon_add_ring(LWGEOM *poly, POINTARRAY *pa, char dimcheck); LWGEOM* wkt_parser_polygon_finalize(LWGEOM *poly, char *dimensionality); LWGEOM* wkt_parser_curvepolygon_new(LWGEOM *ring); LWGEOM* wkt_parser_curvepolygon_add_ring(LWGEOM *poly, LWGEOM *ring); LWGEOM* wkt_parser_curvepolygon_finalize(LWGEOM *poly, char *dimensionality); LWGEOM* wkt_parser_compound_new(LWGEOM *element); LWGEOM* wkt_parser_compound_add_geom(LWGEOM *col, LWGEOM *geom); LWGEOM* wkt_parser_collection_new(LWGEOM *geom); LWGEOM* wkt_parser_collection_add_geom(LWGEOM *col, LWGEOM *geom); LWGEOM* wkt_parser_collection_finalize(int lwtype, LWGEOM *col, char *dimensionality); void wkt_parser_geometry_new(LWGEOM *geom, int32_t srid); lwgeom/src/liblwgeom/lwgeom_debug.c0000644000176200001440000001130314343213114017112 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2004 Refractions Research Inc. * **********************************************************************/ #include "lwgeom_log.h" #include "liblwgeom.h" #include #include /* Place to hold the ZM string used in other summaries */ static char tflags[6]; static char * lwgeom_flagchars(LWGEOM *lwg) { int flagno = 0; if ( FLAGS_GET_Z(lwg->flags) ) tflags[flagno++] = 'Z'; if ( FLAGS_GET_M(lwg->flags) ) tflags[flagno++] = 'M'; if ( FLAGS_GET_BBOX(lwg->flags) ) tflags[flagno++] = 'B'; if ( FLAGS_GET_GEODETIC(lwg->flags) ) tflags[flagno++] = 'G'; if ( lwg->srid != SRID_UNKNOWN ) tflags[flagno++] = 'S'; tflags[flagno] = '\0'; LWDEBUGF(4, "Flags: %s - returning %p", lwg->flags, tflags); return tflags; } /* * Returns an alloced string containing summary for the LWGEOM object */ static char * lwpoint_summary(LWPOINT *point, int offset) { char *result; char *pad=""; char *zmflags = lwgeom_flagchars((LWGEOM*)point); result = (char *)lwalloc(128+offset); snprintf(result, strlen(result), "%*.s%s[%s]", offset, pad, lwtype_name(point->type), zmflags); return result; } static char * lwline_summary(LWLINE *line, int offset) { char *result; char *pad=""; char *zmflags = lwgeom_flagchars((LWGEOM*)line); result = (char *)lwalloc(128+offset); snprintf(result, strlen(result), "%*.s%s[%s] with %d points", offset, pad, lwtype_name(line->type), zmflags, line->points->npoints); return result; } static char * lwcollection_summary(LWCOLLECTION *col, int offset) { size_t size = 128; char *result; char *tmp; uint32_t i; static char *nl = "\n"; char *pad=""; char *zmflags = lwgeom_flagchars((LWGEOM*)col); LWDEBUG(2, "lwcollection_summary called"); result = (char *)lwalloc(size); snprintf(result, strlen(result), "%*.s%s[%s] with %d element%s", offset, pad, lwtype_name(col->type), zmflags, col->ngeoms, col->ngeoms ? ( col->ngeoms > 1 ? "s:\n" : ":\n") : "s"); for (i=0; ingeoms; i++) { tmp = lwgeom_summary(col->geoms[i], offset+2); size += strlen(tmp)+1; result = lwrealloc(result, size); LWDEBUGF(4, "Reallocated %d bytes for result", size); if ( i > 0 ) strcat(result,nl); strcat(result, tmp); lwfree(tmp); } LWDEBUG(3, "lwcollection_summary returning"); return result; } static char * lwpoly_summary(LWPOLY *poly, int offset) { char tmp[256]; size_t size = 64*(poly->nrings+1)+128; char *result; uint32_t i; char *pad=""; static char *nl = "\n"; char *zmflags = lwgeom_flagchars((LWGEOM*)poly); LWDEBUG(2, "lwpoly_summary called"); result = (char *)lwalloc(size); snprintf(result, strlen(result), "%*.s%s[%s] with %i ring%s", offset, pad, lwtype_name(poly->type), zmflags, poly->nrings, poly->nrings ? ( poly->nrings > 1 ? "s:\n" : ":\n") : "s"); for (i=0; inrings; i++) { snprintf(tmp, strlen(tmp), "%s ring %i has %i points", pad, i, poly->rings[i]->npoints); if ( i > 0 ) strcat(result,nl); strcat(result,tmp); } LWDEBUG(3, "lwpoly_summary returning"); return result; } char * lwgeom_summary(const LWGEOM *lwgeom, int offset) { char *result; switch (lwgeom->type) { case POINTTYPE: return lwpoint_summary((LWPOINT *)lwgeom, offset); case CIRCSTRINGTYPE: case TRIANGLETYPE: case LINETYPE: return lwline_summary((LWLINE *)lwgeom, offset); case POLYGONTYPE: return lwpoly_summary((LWPOLY *)lwgeom, offset); case TINTYPE: case MULTISURFACETYPE: case MULTICURVETYPE: case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return lwcollection_summary((LWCOLLECTION *)lwgeom, offset); default: result = (char *)lwalloc(256); snprintf(result, strlen(result), "Object is of unknown type: %d", lwgeom->type); return result; } return NULL; } lwgeom/src/liblwgeom/effectivearea.h0000644000176200001440000000376613773172540017303 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2014 Nicklas Avén * **********************************************************************/ #ifndef _EFFECTIVEAREA_H #define _EFFECTIVEAREA_H 1 #include "liblwgeom_internal.h" #include "lwgeom_log.h" /** This structure is placed in an array with one member per point. It has links into the minheap rtee and keeps track of eliminated points */ typedef struct { double area; int treeindex; int prev; int next; } areanode; /** This structure holds a minheap tree that is used to keep track of what points that has the smallest effective area. When eliminating points the neighbor points has its effective area affected and the minheap helps to resort efficient. */ typedef struct { int maxSize; int usedSize; areanode **key_array; } MINHEAP; /** Structure to hold pointarray and its arealist */ typedef struct { const POINTARRAY *inpts; areanode *initial_arealist; double *res_arealist; } EFFECTIVE_AREAS; EFFECTIVE_AREAS* initiate_effectivearea(const POINTARRAY *inpts); void destroy_effectivearea(EFFECTIVE_AREAS *ea); void ptarray_calc_areas(EFFECTIVE_AREAS *ea,int avoid_collaps, int set_area, double trshld); #endif /* _EFFECTIVEAREA_H */ lwgeom/src/liblwgeom/lwinline.h0000644000176200001440000001360313773172540016322 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2018 Darafei Praliaskouski * Copyright 2017-2018 Daniel Baston * Copyright 2011 Sandro Santilli * Copyright 2011 Paul Ramsey * Copyright 2007-2008 Mark Cave-Ayland * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #if PARANOIA_LEVEL > 0 #include #endif inline static double distance2d_sqr_pt_pt(const POINT2D *p1, const POINT2D *p2) { double hside = p2->x - p1->x; double vside = p2->y - p1->y; return hside * hside + vside * vside; } /* * Size of point represeneted in the POINTARRAY * 16 for 2d, 24 for 3d, 32 for 4d */ static inline size_t ptarray_point_size(const POINTARRAY *pa) { return sizeof(double) * FLAGS_NDIMS(pa->flags); } /* * Get a pointer to Nth point of a POINTARRAY * You'll need to cast it to appropriate dimensioned point. * Note that if you cast to a higher dimensional point you'll * possibly corrupt the POINTARRAY. * * Casting to returned pointer to POINT2D* should be safe, * as gserialized format always keeps the POINTARRAY pointer * aligned to double boundary. * * WARNING: Don't cast this to a POINT! * it would not be reliable due to memory alignment constraints */ static inline uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n) { size_t size; uint8_t *ptr; #if PARANOIA_LEVEL > 0 assert(pa); assert(n <= pa->npoints); assert(n <= pa->maxpoints); #endif size = ptarray_point_size(pa); ptr = pa->serialized_pointlist + size * n; return ptr; } /** * Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ static inline const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n) { return (const POINT2D *)getPoint_internal(pa, n); } /** * Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ static inline const POINT3D * getPoint3d_cp(const POINTARRAY *pa, uint32_t n) { return (const POINT3D *)getPoint_internal(pa, n); } /** * Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ static inline const POINT4D * getPoint4d_cp(const POINTARRAY *pa, uint32_t n) { return (const POINT4D *)getPoint_internal(pa, n); } static inline LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom) { if (!lwgeom) return NULL; if (lwgeom->type == POINTTYPE) return (LWPOINT *)lwgeom; else return NULL; } /** * Return LWTYPE number */ static inline uint32_t lwgeom_get_type(const LWGEOM *geom) { if (!geom) return 0; return geom->type; } static inline int lwpoint_is_empty(const LWPOINT *point) { return !point->point || point->point->npoints < 1; } static inline int lwline_is_empty(const LWLINE *line) { return !line->points || line->points->npoints < 1; } static inline int lwcircstring_is_empty(const LWCIRCSTRING *circ) { return !circ->points || circ->points->npoints < 1; } static inline int lwpoly_is_empty(const LWPOLY *poly) { return poly->nrings < 1 || !poly->rings || !poly->rings[0] || poly->rings[0]->npoints < 1; } static inline int lwtriangle_is_empty(const LWTRIANGLE *triangle) { return !triangle->points || triangle->points->npoints < 1; } static inline int lwgeom_is_empty(const LWGEOM *geom); static inline int lwcollection_is_empty(const LWCOLLECTION *col) { uint32_t i; if (col->ngeoms == 0 || !col->geoms) return LW_TRUE; for (i = 0; i < col->ngeoms; i++) { if (!lwgeom_is_empty(col->geoms[i])) return LW_FALSE; } return LW_TRUE; } /** * Return true or false depending on whether a geometry is an "empty" * geometry (no vertices members) */ static inline int lwgeom_is_empty(const LWGEOM *geom) { switch (geom->type) { case POINTTYPE: return lwpoint_is_empty((LWPOINT *)geom); break; case LINETYPE: return lwline_is_empty((LWLINE *)geom); break; case CIRCSTRINGTYPE: return lwcircstring_is_empty((LWCIRCSTRING *)geom); break; case POLYGONTYPE: return lwpoly_is_empty((LWPOLY *)geom); break; case TRIANGLETYPE: return lwtriangle_is_empty((LWTRIANGLE *)geom); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_is_empty((LWCOLLECTION *)geom); break; default: return LW_FALSE; break; } } /* * This macro is based on PG_FREE_IF_COPY, except that it accepts two pointers. * See PG_FREE_IF_COPY comment in src/include/fmgr.h in postgres source code * for more details. */ #define POSTGIS_FREE_IF_COPY_P(ptrsrc, ptrori) \ do \ { \ if ((Pointer)(ptrsrc) != (Pointer)(ptrori)) \ pfree(ptrsrc); \ } while (0) lwgeom/src/liblwgeom/lwgeodetic_tree.c0000644000176200001440000006707513773172540017655 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2012-2015 Paul Ramsey * Copyright (C) 2012-2015 Sandro Santilli * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeodetic_tree.h" #include "lwgeom_log.h" /* Internal prototype */ static CIRC_NODE* circ_nodes_merge(CIRC_NODE** nodes, int num_nodes); static double circ_tree_distance_tree_internal(const CIRC_NODE* n1, const CIRC_NODE* n2, double threshold, double* min_dist, double* max_dist, GEOGRAPHIC_POINT* closest1, GEOGRAPHIC_POINT* closest2); /** * Internal nodes have their point references set to NULL. */ static inline int circ_node_is_leaf(const CIRC_NODE* node) { return (node->num_nodes == 0); } /** * Recurse from top of node tree and free all children. * does not free underlying point array. */ void circ_tree_free(CIRC_NODE* node) { uint32_t i; if ( ! node ) return; if (node->nodes) { for (i = 0; i < node->num_nodes; i++) circ_tree_free(node->nodes[i]); lwfree(node->nodes); } lwfree(node); } /** * Create a new leaf node, storing pointers back to the end points for later. */ static CIRC_NODE* circ_node_leaf_new(const POINTARRAY* pa, int i) { POINT2D *p1, *p2; POINT3D q1, q2, c; GEOGRAPHIC_POINT g1, g2, gc; CIRC_NODE *node; double diameter; p1 = (POINT2D*)getPoint_internal(pa, i); p2 = (POINT2D*)getPoint_internal(pa, i+1); geographic_point_init(p1->x, p1->y, &g1); geographic_point_init(p2->x, p2->y, &g2); LWDEBUGF(3,"edge #%d (%g %g, %g %g)", i, p1->x, p1->y, p2->x, p2->y); diameter = sphere_distance(&g1, &g2); /* Zero length edge, doesn't get a node */ if ( FP_EQUALS(diameter, 0.0) ) return NULL; /* Allocate */ node = lwalloc(sizeof(CIRC_NODE)); node->p1 = p1; node->p2 = p2; /* Convert ends to X/Y/Z, sum, and normalize to get mid-point */ geog2cart(&g1, &q1); geog2cart(&g2, &q2); vector_sum(&q1, &q2, &c); normalize(&c); cart2geog(&c, &gc); node->center = gc; node->radius = diameter / 2.0; LWDEBUGF(3,"edge #%d CENTER(%g %g) RADIUS=%g", i, gc.lon, gc.lat, node->radius); /* Leaf has no children */ node->num_nodes = 0; node->nodes = NULL; node->edge_num = i; /* Zero out metadata */ node->pt_outside.x = 0.0; node->pt_outside.y = 0.0; node->geom_type = 0; return node; } /** * Return a point node (zero radius, referencing one point) */ static CIRC_NODE* circ_node_leaf_point_new(const POINTARRAY* pa) { CIRC_NODE* tree = lwalloc(sizeof(CIRC_NODE)); tree->p1 = tree->p2 = (POINT2D*)getPoint_internal(pa, 0); geographic_point_init(tree->p1->x, tree->p1->y, &(tree->center)); tree->radius = 0.0; tree->nodes = NULL; tree->num_nodes = 0; tree->edge_num = 0; tree->geom_type = POINTTYPE; tree->pt_outside.x = 0.0; tree->pt_outside.y = 0.0; return tree; } /** * Comparing on geohash ensures that nearby nodes will be close * to each other in the list. */ static int circ_node_compare(const void* v1, const void* v2) { POINT2D p1, p2; unsigned int u1, u2; CIRC_NODE *c1 = *((CIRC_NODE**)v1); CIRC_NODE *c2 = *((CIRC_NODE**)v2); p1.x = rad2deg((c1->center).lon); p1.y = rad2deg((c1->center).lat); p2.x = rad2deg((c2->center).lon); p2.y = rad2deg((c2->center).lat); u1 = geohash_point_as_int(&p1); u2 = geohash_point_as_int(&p2); if ( u1 < u2 ) return -1; if ( u1 > u2 ) return 1; return 0; } /** * Given the centers of two circles, and the offset distance we want to put the new center between them * (calculated as the distance implied by the radii of the inputs and the distance between the centers) * figure out where the new center point is, by getting the direction from c1 to c2 and projecting * from c1 in that direction by the offset distance. */ static int circ_center_spherical(const GEOGRAPHIC_POINT* c1, const GEOGRAPHIC_POINT* c2, double distance, double offset, GEOGRAPHIC_POINT* center) { /* Direction from c1 to c2 */ double dir = sphere_direction(c1, c2, distance); LWDEBUGF(4,"calculating spherical center", dir); LWDEBUGF(4,"dir is %g", dir); /* Catch sphere_direction when it barfs */ if ( isnan(dir) ) return LW_FAILURE; /* Center of new circle is projection from start point, using offset distance*/ return sphere_project(c1, offset, dir, center); } /** * Where the circ_center_spherical() function fails, we need a fall-back. The failures * happen in short arcs, where the spherical distance between two points is practically * the same as the straight-line distance, so our fallback will be to use the straight-line * between the two to calculate the new projected center. For proportions far from 0.5 * this will be increasingly more incorrect. */ static int circ_center_cartesian(const GEOGRAPHIC_POINT* c1, const GEOGRAPHIC_POINT* c2, double distance, double offset, GEOGRAPHIC_POINT* center) { POINT3D p1, p2; POINT3D p1p2, pc; double proportion = offset/distance; LWDEBUG(4,"calculating cartesian center"); geog2cart(c1, &p1); geog2cart(c2, &p2); /* Difference between p2 and p1 */ p1p2.x = p2.x - p1.x; p1p2.y = p2.y - p1.y; p1p2.z = p2.z - p1.z; /* Scale difference to proportion */ p1p2.x *= proportion; p1p2.y *= proportion; p1p2.z *= proportion; /* Add difference to p1 to get approximate center point */ pc.x = p1.x + p1p2.x; pc.y = p1.y + p1p2.y; pc.z = p1.z + p1p2.z; normalize(&pc); /* Convert center point to geographics */ cart2geog(&pc, center); return LW_SUCCESS; } /** * Create a new internal node, calculating the new measure range for the node, * and storing pointers to the child nodes. */ static CIRC_NODE* circ_node_internal_new(CIRC_NODE** c, uint32_t num_nodes) { CIRC_NODE *node = NULL; GEOGRAPHIC_POINT new_center, c1; double new_radius; double offset1, dist, D, r1, ri; uint32_t i, new_geom_type; LWDEBUGF(3, "called with %d nodes --", num_nodes); /* Can't do anything w/ empty input */ if ( num_nodes < 1 ) return node; /* Initialize calculation with values of the first circle */ new_center = c[0]->center; new_radius = c[0]->radius; new_geom_type = c[0]->geom_type; /* Merge each remaining circle into the new circle */ for ( i = 1; i < num_nodes; i++ ) { c1 = new_center; r1 = new_radius; dist = sphere_distance(&c1, &(c[i]->center)); ri = c[i]->radius; /* Promote geometry types up the tree, getting more and more collected */ /* Go until we find a value */ if ( ! new_geom_type ) { new_geom_type = c[i]->geom_type; } /* Promote singleton to a multi-type */ else if ( ! lwtype_is_collection(new_geom_type) ) { /* Anonymous collection if types differ */ if ( new_geom_type != c[i]->geom_type ) { new_geom_type = COLLECTIONTYPE; } else { new_geom_type = lwtype_get_collectiontype(new_geom_type); } } /* If we can't add next feature to this collection cleanly, promote again to anonymous collection */ else if ( new_geom_type != lwtype_get_collectiontype(c[i]->geom_type) ) { new_geom_type = COLLECTIONTYPE; } LWDEBUGF(3, "distance between new (%g %g) and %i (%g %g) is %g", c1.lon, c1.lat, i, c[i]->center.lon, c[i]->center.lat, dist); if ( FP_EQUALS(dist, 0) ) { LWDEBUG(3, " distance between centers is zero"); new_radius = r1 + 2*dist; new_center = c1; } else if ( dist < fabs(r1 - ri) ) { /* new contains next */ if ( r1 > ri ) { LWDEBUG(3, " c1 contains ci"); new_center = c1; new_radius = r1; } /* next contains new */ else { LWDEBUG(3, " ci contains c1"); new_center = c[i]->center; new_radius = ri; } } else { LWDEBUG(3, " calculating new center"); /* New circle diameter */ D = dist + r1 + ri; LWDEBUGF(3," D is %g", D); /* New radius */ new_radius = D / 2.0; /* Distance from cn1 center to the new center */ offset1 = ri + (D - (2.0*r1 + 2.0*ri)) / 2.0; LWDEBUGF(3," offset1 is %g", offset1); /* Sometimes the sphere_direction function fails... this causes the center calculation */ /* to fail too. In that case, we're going to fall back to a cartesian calculation, which */ /* is less exact, so we also have to pad the radius by (hack alert) an arbitrary amount */ /* which is hopefully always big enough to contain the input edges */ if ( circ_center_spherical(&c1, &(c[i]->center), dist, offset1, &new_center) == LW_FAILURE ) { circ_center_cartesian(&c1, &(c[i]->center), dist, offset1, &new_center); new_radius *= 1.1; } } LWDEBUGF(3, " new center is (%g %g) new radius is %g", new_center.lon, new_center.lat, new_radius); } node = lwalloc(sizeof(CIRC_NODE)); node->p1 = NULL; node->p2 = NULL; node->center = new_center; node->radius = new_radius; node->num_nodes = num_nodes; node->nodes = c; node->edge_num = -1; node->geom_type = new_geom_type; node->pt_outside.x = 0.0; node->pt_outside.y = 0.0; return node; } /** * Build a tree of nodes from a point array, one node per edge. */ CIRC_NODE* circ_tree_new(const POINTARRAY* pa) { int num_edges; int i, j; CIRC_NODE **nodes; CIRC_NODE *node; CIRC_NODE *tree; /* Can't do anything with no points */ if ( pa->npoints < 1 ) return NULL; /* Special handling for a single point */ if ( pa->npoints == 1 ) return circ_node_leaf_point_new(pa); /* First create a flat list of nodes, one per edge. */ num_edges = pa->npoints - 1; nodes = lwalloc(sizeof(CIRC_NODE*) * pa->npoints); j = 0; for ( i = 0; i < num_edges; i++ ) { node = circ_node_leaf_new(pa, i); if ( node ) /* Not zero length? */ nodes[j++] = node; } /* Special case: only zero-length edges. Make a point node. */ if ( j == 0 ) { lwfree(nodes); return circ_node_leaf_point_new(pa); } /* Merge the node list pairwise up into a tree */ tree = circ_nodes_merge(nodes, j); /* Free the old list structure, leaving the tree in place */ lwfree(nodes); return tree; } /** * Given a list of nodes, sort them into a spatially consistent * order, then pairwise merge them up into a tree. Should make * handling multipoints and other collections more efficient */ static void circ_nodes_sort(CIRC_NODE** nodes, int num_nodes) { qsort(nodes, num_nodes, sizeof(CIRC_NODE*), circ_node_compare); } static CIRC_NODE* circ_nodes_merge(CIRC_NODE** nodes, int num_nodes) { CIRC_NODE **inodes = NULL; int num_children = num_nodes; int inode_num = 0; int num_parents = 0; int j; /* TODO, roll geom_type *up* as tree is built, changing to collection types as simple types are merged * TODO, change the distance algorithm to drive down to simple types first, test pip on poly/other cases, then test edges */ while( num_children > 1 ) { for ( j = 0; j < num_children; j++ ) { inode_num = (j % CIRC_NODE_SIZE); if ( inode_num == 0 ) inodes = lwalloc(sizeof(CIRC_NODE*)*CIRC_NODE_SIZE); inodes[inode_num] = nodes[j]; if ( inode_num == CIRC_NODE_SIZE-1 ) nodes[num_parents++] = circ_node_internal_new(inodes, CIRC_NODE_SIZE); } /* Clean up any remaining nodes... */ if ( inode_num == 0 ) { /* Promote solo nodes without merging */ nodes[num_parents++] = inodes[0]; lwfree(inodes); } else if ( inode_num < CIRC_NODE_SIZE-1 ) { /* Merge spare nodes */ nodes[num_parents++] = circ_node_internal_new(inodes, inode_num+1); } num_children = num_parents; num_parents = 0; } /* Return a reference to the head of the tree */ return nodes[0]; } /** * Returns a #POINT2D that is a vertex of the input shape */ int circ_tree_get_point(const CIRC_NODE* node, POINT2D* pt) { if ( circ_node_is_leaf(node) ) { pt->x = node->p1->x; pt->y = node->p1->y; return LW_SUCCESS; } else { return circ_tree_get_point(node->nodes[0], pt); } } int circ_tree_get_point_outside(const CIRC_NODE* node, POINT2D* pt) { POINT3D center3d; GEOGRAPHIC_POINT g; if (node->radius >= M_PI) return LW_FAILURE; geog2cart(&(node->center), ¢er3d); vector_scale(¢er3d, -1.0); cart2geog(¢er3d, &g); pt->x = rad2deg(g.lon); pt->y = rad2deg(g.lat); return LW_SUCCESS; } /** * Walk the tree and count intersections between the stab line and the edges. * odd => containment, even => no containment. * KNOWN PROBLEM: Grazings (think of a sharp point, just touching the * stabline) will be counted for one, which will throw off the count. */ int circ_tree_contains_point(const CIRC_NODE* node, const POINT2D* pt, const POINT2D* pt_outside, int level, int* on_boundary) { GEOGRAPHIC_POINT closest; GEOGRAPHIC_EDGE stab_edge, edge; POINT3D S1, S2, E1, E2; double d; uint32_t i, c; /* Construct a stabline edge from our "inside" to our known outside point */ geographic_point_init(pt->x, pt->y, &(stab_edge.start)); geographic_point_init(pt_outside->x, pt_outside->y, &(stab_edge.end)); geog2cart(&(stab_edge.start), &S1); geog2cart(&(stab_edge.end), &S2); LWDEBUGF(3, "%*s entered", level, ""); /* * If the stabline doesn't cross within the radius of a node, there's no * way it can cross. */ LWDEBUGF(3, "%*s :working on node %p, edge_num %d, radius %g, center POINT(%.12g %.12g)", level, "", node, node->edge_num, node->radius, rad2deg(node->center.lon), rad2deg(node->center.lat)); d = edge_distance_to_point(&stab_edge, &(node->center), &closest); LWDEBUGF(3, "%*s :edge_distance_to_point=%g, node_radius=%g", level, "", d, node->radius); if ( FP_LTEQ(d, node->radius) ) { LWDEBUGF(3,"%*s :entering this branch (%p)", level, "", node); /* Return the crossing number of this leaf */ if ( circ_node_is_leaf(node) ) { int inter; LWDEBUGF(3, "%*s :leaf node calculation (edge %d)", level, "", node->edge_num); geographic_point_init(node->p1->x, node->p1->y, &(edge.start)); geographic_point_init(node->p2->x, node->p2->y, &(edge.end)); geog2cart(&(edge.start), &E1); geog2cart(&(edge.end), &E2); inter = edge_intersects(&S1, &S2, &E1, &E2); LWDEBUGF(3, "%*s :inter = %d", level, "", inter); if ( inter & PIR_INTERSECTS ) { LWDEBUGF(3,"%*s ::got stab line edge_intersection with this edge!", level, ""); /* To avoid double counting crossings-at-a-vertex, */ /* always ignore crossings at "lower" ends of edges*/ GEOGRAPHIC_POINT e1, e2; cart2geog(&E1,&e1); cart2geog(&E2,&e2); LWDEBUGF(3,"%*s LINESTRING(%.15g %.15g,%.15g %.15g)", level, "", pt->x, pt->y, pt_outside->x, pt_outside->y ); LWDEBUGF(3,"%*s LINESTRING(%.15g %.15g,%.15g %.15g)", level, "", rad2deg(e1.lon), rad2deg(e1.lat), rad2deg(e2.lon), rad2deg(e2.lat) ); if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR ) { LWDEBUGF(3,"%*s ::rejecting stab line grazing by left-side edge", level, ""); return 0; } else { LWDEBUGF(3,"%*s ::accepting stab line intersection", level, ""); return 1; } } else { LWDEBUGF(3,"%*s edge does not intersect", level, ""); } } /* Or, add up the crossing numbers of all children of this node. */ else { c = 0; for ( i = 0; i < node->num_nodes; i++ ) { LWDEBUGF(3,"%*s calling circ_tree_contains_point on child %d!", level, "", i); c += circ_tree_contains_point(node->nodes[i], pt, pt_outside, level + 1, on_boundary); } return c % 2; } } else { LWDEBUGF(3,"%*s skipping this branch (%p)", level, "", node); } return 0; } static double circ_node_min_distance(const CIRC_NODE* n1, const CIRC_NODE* n2) { double d = sphere_distance(&(n1->center), &(n2->center)); double r1 = n1->radius; double r2 = n2->radius; if ( d < r1 + r2 ) return 0.0; return d - r1 - r2; } static double circ_node_max_distance(const CIRC_NODE *n1, const CIRC_NODE *n2) { return sphere_distance(&(n1->center), &(n2->center)) + n1->radius + n2->radius; } double circ_tree_distance_tree(const CIRC_NODE* n1, const CIRC_NODE* n2, const SPHEROID* spheroid, double threshold) { double min_dist = FLT_MAX; double max_dist = FLT_MAX; GEOGRAPHIC_POINT closest1, closest2; /* Quietly decrease the threshold just a little to avoid cases where */ /* the actual spheroid distance is larger than the sphere distance */ /* causing the return value to be larger than the threshold value */ double threshold_radians = 0.95 * threshold / spheroid->radius; circ_tree_distance_tree_internal(n1, n2, threshold_radians, &min_dist, &max_dist, &closest1, &closest2); /* Spherical case */ if ( spheroid->a == spheroid->b ) { return spheroid->radius * sphere_distance(&closest1, &closest2); } else { return spheroid_distance(&closest1, &closest2, spheroid); } } /*********************************************************************** * Internal node sorting routine to make distance calculations faster? */ struct sort_node { CIRC_NODE *node; double d; }; static int circ_nodes_sort_cmp(const void *a, const void *b) { struct sort_node *node_a = (struct sort_node *)(a); struct sort_node *node_b = (struct sort_node *)(b); if (node_a->d < node_b->d) return -1; else if (node_a->d > node_b->d) return 1; else return 0; } static void circ_internal_nodes_sort(CIRC_NODE **nodes, uint32_t num_nodes, const CIRC_NODE *target_node) { uint32_t i; struct sort_node sort_nodes[CIRC_NODE_SIZE]; /* Copy incoming nodes into sorting array and calculate */ /* distance to the target node */ for (i = 0; i < num_nodes; i++) { sort_nodes[i].node = nodes[i]; sort_nodes[i].d = sphere_distance(&(nodes[i]->center), &(target_node->center)); } /* Sort the nodes and copy the result back into the input array */ qsort(sort_nodes, num_nodes, sizeof(struct sort_node), circ_nodes_sort_cmp); for (i = 0; i < num_nodes; i++) { nodes[i] = sort_nodes[i].node; } return; } /***********************************************************************/ static double circ_tree_distance_tree_internal(const CIRC_NODE* n1, const CIRC_NODE* n2, double threshold, double* min_dist, double* max_dist, GEOGRAPHIC_POINT* closest1, GEOGRAPHIC_POINT* closest2) { double max; double d, d_min; uint32_t i; LWDEBUGF(4, "entered, min_dist=%.8g max_dist=%.8g, type1=%d, type2=%d", *min_dist, *max_dist, n1->geom_type, n2->geom_type); // printf("-==-\n"); // circ_tree_print(n1, 0); // printf("--\n"); // circ_tree_print(n2, 0); /* Short circuit if we've already hit the minimum */ if( *min_dist < threshold || *min_dist == 0.0 ) return *min_dist; /* If your minimum is greater than anyone's maximum, you can't hold the winner */ if( circ_node_min_distance(n1, n2) > *max_dist ) { LWDEBUGF(4, "pruning pair %p, %p", n1, n2); return FLT_MAX; } /* If your maximum is a new low, we'll use that as our new global tolerance */ max = circ_node_max_distance(n1, n2); LWDEBUGF(5, "max %.8g", max); if( max < *max_dist ) *max_dist = max; /* Polygon on one side, primitive type on the other. Check for point-in-polygon */ /* short circuit. */ if ( n1->geom_type == POLYGONTYPE && n2->geom_type && ! lwtype_is_collection(n2->geom_type) ) { POINT2D pt; circ_tree_get_point(n2, &pt); LWDEBUGF(4, "n1 is polygon, testing if contains (%.5g,%.5g)", pt.x, pt.y); if ( circ_tree_contains_point(n1, &pt, &(n1->pt_outside), 0, NULL) ) { LWDEBUG(4, "it does"); *min_dist = 0.0; geographic_point_init(pt.x, pt.y, closest1); geographic_point_init(pt.x, pt.y, closest2); return *min_dist; } } /* Polygon on one side, primitive type on the other. Check for point-in-polygon */ /* short circuit. */ if ( n2->geom_type == POLYGONTYPE && n1->geom_type && ! lwtype_is_collection(n1->geom_type) ) { POINT2D pt; circ_tree_get_point(n1, &pt); LWDEBUGF(4, "n2 is polygon, testing if contains (%.5g,%.5g)", pt.x, pt.y); if ( circ_tree_contains_point(n2, &pt, &(n2->pt_outside), 0, NULL) ) { LWDEBUG(4, "it does"); geographic_point_init(pt.x, pt.y, closest1); geographic_point_init(pt.x, pt.y, closest2); *min_dist = 0.0; return *min_dist; } } /* Both leaf nodes, do a real distance calculation */ if( circ_node_is_leaf(n1) && circ_node_is_leaf(n2) ) { double d; GEOGRAPHIC_POINT close1, close2; LWDEBUGF(4, "testing leaf pair [%d], [%d]", n1->edge_num, n2->edge_num); /* One of the nodes is a point */ if ( n1->p1 == n1->p2 || n2->p1 == n2->p2 ) { GEOGRAPHIC_EDGE e; GEOGRAPHIC_POINT gp1, gp2; /* Both nodes are points! */ if ( n1->p1 == n1->p2 && n2->p1 == n2->p2 ) { geographic_point_init(n1->p1->x, n1->p1->y, &gp1); geographic_point_init(n2->p1->x, n2->p1->y, &gp2); close1 = gp1; close2 = gp2; d = sphere_distance(&gp1, &gp2); } /* Node 1 is a point */ else if ( n1->p1 == n1->p2 ) { geographic_point_init(n1->p1->x, n1->p1->y, &gp1); geographic_point_init(n2->p1->x, n2->p1->y, &(e.start)); geographic_point_init(n2->p2->x, n2->p2->y, &(e.end)); close1 = gp1; d = edge_distance_to_point(&e, &gp1, &close2); } /* Node 2 is a point */ else { geographic_point_init(n2->p1->x, n2->p1->y, &gp1); geographic_point_init(n1->p1->x, n1->p1->y, &(e.start)); geographic_point_init(n1->p2->x, n1->p2->y, &(e.end)); close1 = gp1; d = edge_distance_to_point(&e, &gp1, &close2); } LWDEBUGF(4, " got distance %g", d); } /* Both nodes are edges */ else { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g; POINT3D A1, A2, B1, B2; geographic_point_init(n1->p1->x, n1->p1->y, &(e1.start)); geographic_point_init(n1->p2->x, n1->p2->y, &(e1.end)); geographic_point_init(n2->p1->x, n2->p1->y, &(e2.start)); geographic_point_init(n2->p2->x, n2->p2->y, &(e2.end)); geog2cart(&(e1.start), &A1); geog2cart(&(e1.end), &A2); geog2cart(&(e2.start), &B1); geog2cart(&(e2.end), &B2); if ( edge_intersects(&A1, &A2, &B1, &B2) ) { d = 0.0; edge_intersection(&e1, &e2, &g); close1 = close2 = g; } else { d = edge_distance_to_edge(&e1, &e2, &close1, &close2); } LWDEBUGF(4, "edge_distance_to_edge returned %g", d); } if ( d < *min_dist ) { *min_dist = d; *closest1 = close1; *closest2 = close2; } return d; } else { d_min = FLT_MAX; /* Drive the recursion into the COLLECTION types first so we end up with */ /* pairings of primitive geometries that can be forced into the point-in-polygon */ /* tests above. */ if ( n1->geom_type && lwtype_is_collection(n1->geom_type) ) { circ_internal_nodes_sort(n1->nodes, n1->num_nodes, n2); for ( i = 0; i < n1->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n1->nodes[i], n2, threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } } else if ( n2->geom_type && lwtype_is_collection(n2->geom_type) ) { circ_internal_nodes_sort(n2->nodes, n2->num_nodes, n1); for ( i = 0; i < n2->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n1, n2->nodes[i], threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } } else if ( ! circ_node_is_leaf(n1) ) { circ_internal_nodes_sort(n1->nodes, n1->num_nodes, n2); for ( i = 0; i < n1->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n1->nodes[i], n2, threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } } else if ( ! circ_node_is_leaf(n2) ) { circ_internal_nodes_sort(n2->nodes, n2->num_nodes, n1); for ( i = 0; i < n2->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n1, n2->nodes[i], threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } } else { /* Never get here */ } return d_min; } } void circ_tree_print(const CIRC_NODE* node, int depth) { /* uint32_t i; if (circ_node_is_leaf(node)) { printf("%*s[%d] C(%.5g %.5g) R(%.5g) ((%.5g %.5g),(%.5g,%.5g))", 3*depth + 6, "NODE", node->edge_num, node->center.lon, node->center.lat, node->radius, node->p1->x, node->p1->y, node->p2->x, node->p2->y ); if ( node->geom_type ) { printf(" %s", lwtype_name(node->geom_type)); } if ( node->geom_type == POLYGONTYPE ) { printf(" O(%.5g %.5g)", node->pt_outside.x, node->pt_outside.y); } printf("\n"); } else { printf("%*s C(%.5g %.5g) R(%.5g)", 3*depth + 6, "NODE", node->center.lon, node->center.lat, node->radius ); if ( node->geom_type ) { printf(" %s", lwtype_name(node->geom_type)); } if ( node->geom_type == POLYGONTYPE ) { printf(" O(%.15g %.15g)", node->pt_outside.x, node->pt_outside.y); } printf("\n"); } for ( i = 0; i < node->num_nodes; i++ ) { circ_tree_print(node->nodes[i], depth + 1); } */ return; } static CIRC_NODE* lwpoint_calculate_circ_tree(const LWPOINT* lwpoint) { CIRC_NODE* node; node = circ_tree_new(lwpoint->point); node->geom_type = lwgeom_get_type((LWGEOM*)lwpoint);; return node; } static CIRC_NODE* lwline_calculate_circ_tree(const LWLINE* lwline) { CIRC_NODE* node; node = circ_tree_new(lwline->points); node->geom_type = lwgeom_get_type((LWGEOM*)lwline); return node; } static CIRC_NODE* lwpoly_calculate_circ_tree(const LWPOLY* lwpoly) { uint32_t i = 0, j = 0; CIRC_NODE** nodes; CIRC_NODE* node; /* One ring? Handle it like a line. */ if ( lwpoly->nrings == 1 ) { node = circ_tree_new(lwpoly->rings[0]); } else { /* Calculate a tree for each non-trivial ring of the polygon */ nodes = lwalloc(lwpoly->nrings * sizeof(CIRC_NODE*)); for ( i = 0; i < lwpoly->nrings; i++ ) { node = circ_tree_new(lwpoly->rings[i]); if ( node ) nodes[j++] = node; } /* Put the trees into a spatially correlated order */ circ_nodes_sort(nodes, j); /* Merge the trees pairwise up to a parent node and return */ node = circ_nodes_merge(nodes, j); /* Don't need the working list any more */ lwfree(nodes); } /* Metadata about polygons, we need this to apply P-i-P tests */ /* selectively when doing distance calculations */ node->geom_type = lwgeom_get_type((LWGEOM*)lwpoly); lwpoly_pt_outside(lwpoly, &(node->pt_outside)); return node; } static CIRC_NODE* lwcollection_calculate_circ_tree(const LWCOLLECTION* lwcol) { uint32_t i = 0, j = 0; CIRC_NODE** nodes; CIRC_NODE* node; /* One geometry? Done! */ if ( lwcol->ngeoms == 1 ) return lwgeom_calculate_circ_tree(lwcol->geoms[0]); /* Calculate a tree for each sub-geometry*/ nodes = lwalloc(lwcol->ngeoms * sizeof(CIRC_NODE*)); for ( i = 0; i < lwcol->ngeoms; i++ ) { node = lwgeom_calculate_circ_tree(lwcol->geoms[i]); if ( node ) nodes[j++] = node; } /* Put the trees into a spatially correlated order */ circ_nodes_sort(nodes, j); /* Merge the trees pairwise up to a parent node and return */ node = circ_nodes_merge(nodes, j); /* Don't need the working list any more */ lwfree(nodes); node->geom_type = lwgeom_get_type((LWGEOM*)lwcol); return node; } CIRC_NODE* lwgeom_calculate_circ_tree(const LWGEOM* lwgeom) { if ( lwgeom_is_empty(lwgeom) ) return NULL; switch ( lwgeom->type ) { case POINTTYPE: return lwpoint_calculate_circ_tree((LWPOINT*)lwgeom); case LINETYPE: return lwline_calculate_circ_tree((LWLINE*)lwgeom); case POLYGONTYPE: return lwpoly_calculate_circ_tree((LWPOLY*)lwgeom); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: return lwcollection_calculate_circ_tree((LWCOLLECTION*)lwgeom); default: lwerror("Unable to calculate spherical index tree for type %s", lwtype_name(lwgeom->type)); return NULL; } } lwgeom/src/liblwgeom/lwunionfind.h0000644000176200001440000000414613773172540017037 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * **********************************************************************/ #ifndef _LWUNIONFIND #define _LWUNIONFIND 1 #include "liblwgeom.h" typedef struct { uint32_t* clusters; uint32_t* cluster_sizes; uint32_t num_clusters; uint32_t N; } UNIONFIND; /* Allocate a UNIONFIND structure of capacity N */ UNIONFIND* UF_create(uint32_t N); /* Release memory associated with UNIONFIND structure */ void UF_destroy(UNIONFIND* uf); /* Identify the cluster id associated with specified component id */ uint32_t UF_find(UNIONFIND* uf, uint32_t i); /* Get the size of the cluster associated with the specified component id */ uint32_t UF_size(UNIONFIND* uf, uint32_t i); /* Merge the clusters that contain the two specified component ids */ void UF_union(UNIONFIND* uf, uint32_t i, uint32_t j); /* Return an array of component ids, where components that are in the * same cluster are contiguous in the array */ uint32_t* UF_ordered_by_cluster(UNIONFIND* uf); /* Replace the cluster ids in a UNIONFIND with sequential ids starting at one. * If is_in_cluster array is provided, it will be used to skip any indexes * that are not in a cluster. * */ uint32_t* UF_get_collapsed_cluster_ids(UNIONFIND* uf, const char* is_in_cluster); #endif lwgeom/src/liblwgeom/ptarray.c0000644000176200001440000013646713773172540016174 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" int ptarray_has_z(const POINTARRAY *pa) { if ( ! pa ) return LW_FALSE; return FLAGS_GET_Z(pa->flags); } int ptarray_has_m(const POINTARRAY *pa) { if ( ! pa ) return LW_FALSE; return FLAGS_GET_M(pa->flags); } POINTARRAY* ptarray_construct(char hasz, char hasm, uint32_t npoints) { POINTARRAY *pa = ptarray_construct_empty(hasz, hasm, npoints); pa->npoints = npoints; return pa; } POINTARRAY* ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints) { POINTARRAY *pa = lwalloc(sizeof(POINTARRAY)); pa->serialized_pointlist = NULL; /* Set our dimensionality info on the bitmap */ pa->flags = lwflags(hasz, hasm, 0); /* We will be allocating a bit of room */ pa->npoints = 0; pa->maxpoints = maxpoints; /* Allocate the coordinate array */ if ( maxpoints > 0 ) pa->serialized_pointlist = lwalloc(maxpoints * ptarray_point_size(pa)); else pa->serialized_pointlist = NULL; return pa; } /* * Add a point into a pointarray. Only adds as many dimensions as the * pointarray supports. */ int ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where) { if (!pa || !p) return LW_FAILURE; size_t point_size = ptarray_point_size(pa); LWDEBUGF(5,"pa = %p; p = %p; where = %d", pa, p, where); LWDEBUGF(5,"pa->npoints = %d; pa->maxpoints = %d", pa->npoints, pa->maxpoints); if ( FLAGS_GET_READONLY(pa->flags) ) { lwerror("ptarray_insert_point: called on read-only point array"); return LW_FAILURE; } /* Error on invalid offset value */ if ( where > pa->npoints ) { lwerror("ptarray_insert_point: offset out of range (%d)", where); return LW_FAILURE; } /* If we have no storage, let's allocate some */ if( pa->maxpoints == 0 || ! pa->serialized_pointlist ) { pa->maxpoints = 32; pa->npoints = 0; pa->serialized_pointlist = lwalloc(ptarray_point_size(pa) * pa->maxpoints); } /* Error out if we have a bad situation */ if ( pa->npoints > pa->maxpoints ) { lwerror("npoints (%d) is greater than maxpoints (%d)", pa->npoints, pa->maxpoints); return LW_FAILURE; } /* Check if we have enough storage, add more if necessary */ if( pa->npoints == pa->maxpoints ) { pa->maxpoints *= 2; pa->serialized_pointlist = lwrealloc(pa->serialized_pointlist, ptarray_point_size(pa) * pa->maxpoints); } /* Make space to insert the new point */ if( where < pa->npoints ) { size_t copy_size = point_size * (pa->npoints - where); memmove(getPoint_internal(pa, where+1), getPoint_internal(pa, where), copy_size); LWDEBUGF(5,"copying %d bytes to start vertex %d from start vertex %d", copy_size, where+1, where); } /* We have one more point */ ++pa->npoints; /* Copy the new point into the gap */ ptarray_set_point4d(pa, where, p); LWDEBUGF(5,"copying new point to start vertex %d", point_size, where); return LW_SUCCESS; } int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int repeated_points) { /* Check for pathology */ if( ! pa || ! pt ) { lwerror("ptarray_append_point: null input"); return LW_FAILURE; } /* Check for duplicate end point */ if ( repeated_points == LW_FALSE && pa->npoints > 0 ) { POINT4D tmp; getPoint4d_p(pa, pa->npoints-1, &tmp); LWDEBUGF(4,"checking for duplicate end point (pt = POINT(%g %g) pa->npoints-q = POINT(%g %g))",pt->x,pt->y,tmp.x,tmp.y); /* Return LW_SUCCESS and do nothing else if previous point in list is equal to this one */ if ( (pt->x == tmp.x) && (pt->y == tmp.y) && (FLAGS_GET_Z(pa->flags) ? pt->z == tmp.z : 1) && (FLAGS_GET_M(pa->flags) ? pt->m == tmp.m : 1) ) { return LW_SUCCESS; } } /* Append is just a special case of insert */ return ptarray_insert_point(pa, pt, pa->npoints); } int ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance) { unsigned int poff = 0; unsigned int npoints; unsigned int ncap; unsigned int ptsize; /* Check for pathology */ if( ! pa1 || ! pa2 ) { lwerror("ptarray_append_ptarray: null input"); return LW_FAILURE; } npoints = pa2->npoints; if ( ! npoints ) return LW_SUCCESS; /* nothing more to do */ if( FLAGS_GET_READONLY(pa1->flags) ) { lwerror("ptarray_append_ptarray: target pointarray is read-only"); return LW_FAILURE; } if( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) ) { lwerror("ptarray_append_ptarray: appending mixed dimensionality is not allowed"); return LW_FAILURE; } ptsize = ptarray_point_size(pa1); /* Check for duplicate end point */ if ( pa1->npoints ) { POINT2D tmp1, tmp2; getPoint2d_p(pa1, pa1->npoints-1, &tmp1); getPoint2d_p(pa2, 0, &tmp2); /* If the end point and start point are the same, then don't copy start point */ if (p2d_same(&tmp1, &tmp2)) { poff = 1; --npoints; } else if ( gap_tolerance == 0 || ( gap_tolerance > 0 && distance2d_pt_pt(&tmp1, &tmp2) > gap_tolerance ) ) { lwerror("Second line start point too far from first line end point"); return LW_FAILURE; } } /* Check if we need extra space */ ncap = pa1->npoints + npoints; if ( pa1->maxpoints < ncap ) { pa1->maxpoints = ncap > pa1->maxpoints*2 ? ncap : pa1->maxpoints*2; pa1->serialized_pointlist = lwrealloc(pa1->serialized_pointlist, ptsize * pa1->maxpoints); } memcpy(getPoint_internal(pa1, pa1->npoints), getPoint_internal(pa2, poff), ptsize * npoints); pa1->npoints = ncap; return LW_SUCCESS; } /* * Add a point into a pointarray. Only adds as many dimensions as the * pointarray supports. */ int ptarray_remove_point(POINTARRAY *pa, uint32_t where) { /* Check for pathology */ if( ! pa ) { lwerror("ptarray_remove_point: null input"); return LW_FAILURE; } /* Error on invalid offset value */ if ( where >= pa->npoints ) { lwerror("ptarray_remove_point: offset out of range (%d)", where); return LW_FAILURE; } /* If the point is any but the last, we need to copy the data back one point */ if (where < pa->npoints - 1) memmove(getPoint_internal(pa, where), getPoint_internal(pa, where + 1), ptarray_point_size(pa) * (pa->npoints - where - 1)); /* We have one less point */ pa->npoints--; return LW_SUCCESS; } /** * Build a new #POINTARRAY, but on top of someone else's ordinate array. * Flag as read-only, so that ptarray_free() does not free the serialized_ptlist */ POINTARRAY* ptarray_construct_reference_data(char hasz, char hasm, uint32_t npoints, uint8_t *ptlist) { POINTARRAY *pa = lwalloc(sizeof(POINTARRAY)); LWDEBUGF(5, "hasz = %d, hasm = %d, npoints = %d, ptlist = %p", hasz, hasm, npoints, ptlist); pa->flags = lwflags(hasz, hasm, 0); FLAGS_SET_READONLY(pa->flags, 1); /* We don't own this memory, so we can't alter or free it. */ pa->npoints = npoints; pa->maxpoints = npoints; pa->serialized_pointlist = ptlist; return pa; } POINTARRAY* ptarray_construct_copy_data(char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist) { POINTARRAY *pa = lwalloc(sizeof(POINTARRAY)); pa->flags = lwflags(hasz, hasm, 0); pa->npoints = npoints; pa->maxpoints = npoints; if ( npoints > 0 ) { pa->serialized_pointlist = lwalloc(ptarray_point_size(pa) * npoints); memcpy(pa->serialized_pointlist, ptlist, ptarray_point_size(pa) * npoints); } else { pa->serialized_pointlist = NULL; } return pa; } void ptarray_free(POINTARRAY *pa) { if (pa) { if (pa->serialized_pointlist && (!FLAGS_GET_READONLY(pa->flags))) lwfree(pa->serialized_pointlist); lwfree(pa); } } void ptarray_reverse_in_place(POINTARRAY *pa) { if (!pa->npoints) return; uint32_t i; uint32_t last = pa->npoints - 1; uint32_t mid = pa->npoints / 2; double *d = (double*)(pa->serialized_pointlist); int j; int ndims = FLAGS_NDIMS(pa->flags); for (i = 0; i < mid; i++) { for (j = 0; j < ndims; j++) { double buf; buf = d[i*ndims+j]; d[i*ndims+j] = d[(last-i)*ndims+j]; d[(last-i)*ndims+j] = buf; } } return; } /** * Reverse X and Y axis on a given POINTARRAY */ POINTARRAY* ptarray_flip_coordinates(POINTARRAY *pa) { uint32_t i; double d; POINT4D p; for (i=0 ; i < pa->npoints ; i++) { getPoint4d_p(pa, i, &p); d = p.y; p.y = p.x; p.x = d; ptarray_set_point4d(pa, i, &p); } return pa; } void ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2) { uint32_t i; double d, *dp1, *dp2; POINT4D p; dp1 = ((double*)&p)+(unsigned)o1; dp2 = ((double*)&p)+(unsigned)o2; for (i=0 ; i < pa->npoints ; i++) { getPoint4d_p(pa, i, &p); d = *dp2; *dp2 = *dp1; *dp1 = d; ptarray_set_point4d(pa, i, &p); } } /** * @brief Returns a modified #POINTARRAY so that no segment is * longer than the given distance (computed using 2d). * * Every input point is kept. * Z and M values for added points (if needed) are set proportionally. */ POINTARRAY * ptarray_segmentize2d(const POINTARRAY *ipa, double dist) { double segdist; POINT4D p1, p2; POINT4D pbuf; POINTARRAY *opa; uint32_t i, j, nseg; int hasz = FLAGS_GET_Z(ipa->flags); int hasm = FLAGS_GET_M(ipa->flags); pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0; /* Initial storage */ opa = ptarray_construct_empty(hasz, hasm, ipa->npoints); /* Add first point */ getPoint4d_p(ipa, 0, &p1); ptarray_append_point(opa, &p1, LW_FALSE); /* Loop on all other input points */ for (i = 1; i < ipa->npoints; i++) { /* * We use these pointers to avoid * "strict-aliasing rules break" warning raised * by gcc (3.3 and up). * * It looks that casting a variable address (also * referred to as "type-punned pointer") * breaks those "strict" rules. */ POINT4D *p1ptr=&p1, *p2ptr=&p2; double segments; getPoint4d_p(ipa, i, &p2); segdist = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr); /* Split input segment into shorter even chunks */ segments = ceil(segdist / dist); /* Uses INT32_MAX instead of UINT32_MAX to be safe that it fits */ if (segments >= INT32_MAX) { lwnotice("%s:%d - %s: Too many segments required (%e)", __FILE__, __LINE__,__func__, segments); ptarray_free(opa); return NULL; } nseg = segments; for (j = 1; j < nseg; j++) { pbuf.x = p1.x + (p2.x - p1.x) * j / nseg; pbuf.y = p1.y + (p2.y - p1.y) * j / nseg; if (hasz) pbuf.z = p1.z + (p2.z - p1.z) * j / nseg; if (hasm) pbuf.m = p1.m + (p2.m - p1.m) * j / nseg; ptarray_append_point(opa, &pbuf, LW_FALSE); LW_ON_INTERRUPT(ptarray_free(opa); return NULL); } ptarray_append_point(opa, &p2, (ipa->npoints == 2) ? LW_TRUE : LW_FALSE); p1 = p2; LW_ON_INTERRUPT(ptarray_free(opa); return NULL); } return opa; } char ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2) { uint32_t i; size_t ptsize; if ( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) ) return LW_FALSE; LWDEBUG(5,"dimensions are the same"); if ( pa1->npoints != pa2->npoints ) return LW_FALSE; LWDEBUG(5,"npoints are the same"); ptsize = ptarray_point_size(pa1); LWDEBUGF(5, "ptsize = %d", ptsize); for (i=0; inpoints; i++) { if ( memcmp(getPoint_internal(pa1, i), getPoint_internal(pa2, i), ptsize) ) return LW_FALSE; LWDEBUGF(5,"point #%d is the same",i); } return LW_TRUE; } POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where) { POINTARRAY *ret; POINT4D pbuf; size_t ptsize = ptarray_point_size(pa); LWDEBUGF(3, "pa %x p %x size %d where %d", pa, p, pdims, where); if ( pdims < 2 || pdims > 4 ) { lwerror("ptarray_addPoint: point dimension out of range (%d)", pdims); return NULL; } if ( where > pa->npoints ) { lwerror("ptarray_addPoint: offset out of range (%d)", where); return NULL; } LWDEBUG(3, "called with a %dD point"); pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0.0; memcpy((uint8_t *)&pbuf, p, pdims*sizeof(double)); LWDEBUG(3, "initialized point buffer"); ret = ptarray_construct(FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags), pa->npoints+1); if ( where ) { memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*where); } memcpy(getPoint_internal(ret, where), (uint8_t *)&pbuf, ptsize); if ( where+1 != ret->npoints ) { memcpy(getPoint_internal(ret, where+1), getPoint_internal(pa, where), ptsize*(pa->npoints-where)); } return ret; } POINTARRAY * ptarray_removePoint(POINTARRAY *pa, uint32_t which) { POINTARRAY *ret; size_t ptsize = ptarray_point_size(pa); LWDEBUGF(3, "pa %x which %d", pa, which); #if PARANOIA_LEVEL > 0 if ( which > pa->npoints-1 ) { lwerror("%s [%d] offset (%d) out of range (%d..%d)", __FILE__, __LINE__, which, 0, pa->npoints-1); return NULL; } if ( pa->npoints < 3 ) { lwerror("%s [%d] can't remove a point from a 2-vertex POINTARRAY", __FILE__, __LINE__); return NULL; } #endif ret = ptarray_construct(FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags), pa->npoints-1); /* copy initial part */ if ( which ) { memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*which); } /* copy final part */ if ( which < pa->npoints-1 ) { memcpy(getPoint_internal(ret, which), getPoint_internal(pa, which+1), ptsize*(pa->npoints-which-1)); } return ret; } POINTARRAY * ptarray_merge(POINTARRAY *pa1, POINTARRAY *pa2) { POINTARRAY *pa; size_t ptsize = ptarray_point_size(pa1); if (FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags)) lwerror("ptarray_cat: Mixed dimension"); pa = ptarray_construct( FLAGS_GET_Z(pa1->flags), FLAGS_GET_M(pa1->flags), pa1->npoints + pa2->npoints); memcpy( getPoint_internal(pa, 0), getPoint_internal(pa1, 0), ptsize*(pa1->npoints)); memcpy( getPoint_internal(pa, pa1->npoints), getPoint_internal(pa2, 0), ptsize*(pa2->npoints)); ptarray_free(pa1); ptarray_free(pa2); return pa; } /** * @brief Deep clone a pointarray (also clones serialized pointlist) */ POINTARRAY * ptarray_clone_deep(const POINTARRAY *in) { POINTARRAY *out = lwalloc(sizeof(POINTARRAY)); LWDEBUG(3, "ptarray_clone_deep called."); out->flags = in->flags; out->npoints = in->npoints; out->maxpoints = in->npoints; FLAGS_SET_READONLY(out->flags, 0); if (!in->npoints) { // Avoid calling lwalloc of 0 bytes out->serialized_pointlist = NULL; } else { size_t size = in->npoints * ptarray_point_size(in); out->serialized_pointlist = lwalloc(size); memcpy(out->serialized_pointlist, in->serialized_pointlist, size); } return out; } /** * @brief Clone a POINTARRAY object. Serialized pointlist is not copied. */ POINTARRAY * ptarray_clone(const POINTARRAY *in) { POINTARRAY *out = lwalloc(sizeof(POINTARRAY)); LWDEBUG(3, "ptarray_clone called."); out->flags = in->flags; out->npoints = in->npoints; out->maxpoints = in->maxpoints; FLAGS_SET_READONLY(out->flags, 1); out->serialized_pointlist = in->serialized_pointlist; return out; } /** * Check for ring closure using whatever dimensionality is declared on the * pointarray. */ int ptarray_is_closed(const POINTARRAY *in) { if (!in) { lwerror("ptarray_is_closed: called with null point array"); return 0; } if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */ return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), ptarray_point_size(in)); } int ptarray_is_closed_2d(const POINTARRAY *in) { if (!in) { lwerror("ptarray_is_closed_2d: called with null point array"); return 0; } if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */ return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), sizeof(POINT2D) ); } int ptarray_is_closed_3d(const POINTARRAY *in) { if (!in) { lwerror("ptarray_is_closed_3d: called with null point array"); return 0; } if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */ return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), sizeof(POINT3D) ); } int ptarray_is_closed_z(const POINTARRAY *in) { if ( FLAGS_GET_Z(in->flags) ) return ptarray_is_closed_3d(in); else return ptarray_is_closed_2d(in); } /** * Return 1 if the point is inside the POINTARRAY, -1 if it is outside, * and 0 if it is on the boundary. */ int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt) { return ptarray_contains_point_partial(pa, pt, LW_TRUE, NULL); } int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number) { int wn = 0; uint32_t i; double side; const POINT2D *seg1; const POINT2D *seg2; double ymin, ymax; seg1 = getPoint2d_cp(pa, 0); seg2 = getPoint2d_cp(pa, pa->npoints-1); if ( check_closed && ! p2d_same(seg1, seg2) ) lwerror("ptarray_contains_point called on unclosed ring"); for ( i=1; i < pa->npoints; i++ ) { seg2 = getPoint2d_cp(pa, i); /* Zero length segments are ignored. */ if ( seg1->x == seg2->x && seg1->y == seg2->y ) { seg1 = seg2; continue; } ymin = FP_MIN(seg1->y, seg2->y); ymax = FP_MAX(seg1->y, seg2->y); /* Only test segments in our vertical range */ if ( pt->y > ymax || pt->y < ymin ) { seg1 = seg2; continue; } side = lw_segment_side(seg1, seg2, pt); /* * A point on the boundary of a ring is not contained. * WAS: if (fabs(side) < 1e-12), see #852 */ if ( (side == 0) && lw_pt_in_seg(pt, seg1, seg2) ) { return LW_BOUNDARY; } /* * If the point is to the left of the line, and it's rising, * then the line is to the right of the point and * circling counter-clockwise, so increment. */ if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) ) { wn++; } /* * If the point is to the right of the line, and it's falling, * then the line is to the right of the point and circling * clockwise, so decrement. */ else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } seg1 = seg2; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return LW_OUTSIDE; } /* Inside */ return LW_INSIDE; } /** * For POINTARRAYs representing CIRCULARSTRINGS. That is, linked triples * with each triple being control points of a circular arc. Such * POINTARRAYs have an odd number of vertices. * * Return 1 if the point is inside the POINTARRAY, -1 if it is outside, * and 0 if it is on the boundary. */ int ptarrayarc_contains_point(const POINTARRAY *pa, const POINT2D *pt) { return ptarrayarc_contains_point_partial(pa, pt, LW_TRUE /* Check closed*/, NULL); } int ptarrayarc_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number) { int wn = 0; uint32_t i; int side; const POINT2D *seg1; const POINT2D *seg2; const POINT2D *seg3; GBOX gbox; /* Check for not an arc ring (always have odd # of points) */ if ( (pa->npoints % 2) == 0 ) { lwerror("ptarrayarc_contains_point called with even number of points"); return LW_OUTSIDE; } /* Check for not an arc ring (always have >= 3 points) */ if ( pa->npoints < 3 ) { lwerror("ptarrayarc_contains_point called too-short pointarray"); return LW_OUTSIDE; } /* Check for unclosed case */ seg1 = getPoint2d_cp(pa, 0); seg3 = getPoint2d_cp(pa, pa->npoints-1); if ( check_closed && ! p2d_same(seg1, seg3) ) { lwerror("ptarrayarc_contains_point called on unclosed ring"); return LW_OUTSIDE; } /* OK, it's closed. Is it just one circle? */ else if ( p2d_same(seg1, seg3) && pa->npoints == 3 ) { double radius, d; POINT2D c; seg2 = getPoint2d_cp(pa, 1); /* Wait, it's just a point, so it can't contain anything */ if ( lw_arc_is_pt(seg1, seg2, seg3) ) return LW_OUTSIDE; /* See if the point is within the circle radius */ radius = lw_arc_center(seg1, seg2, seg3, &c); d = distance2d_pt_pt(pt, &c); if ( FP_EQUALS(d, radius) ) return LW_BOUNDARY; /* Boundary of circle */ else if ( d < radius ) return LW_INSIDE; /* Inside circle */ else return LW_OUTSIDE; /* Outside circle */ } else if ( p2d_same(seg1, pt) || p2d_same(seg3, pt) ) { return LW_BOUNDARY; /* Boundary case */ } /* Start on the ring */ seg1 = getPoint2d_cp(pa, 0); for ( i=1; i < pa->npoints; i += 2 ) { seg2 = getPoint2d_cp(pa, i); seg3 = getPoint2d_cp(pa, i+1); /* Catch an easy boundary case */ if( p2d_same(seg3, pt) ) return LW_BOUNDARY; /* Skip arcs that have no size */ if ( lw_arc_is_pt(seg1, seg2, seg3) ) { seg1 = seg3; continue; } /* Only test segments in our vertical range */ lw_arc_calculate_gbox_cartesian_2d(seg1, seg2, seg3, &gbox); if ( pt->y > gbox.ymax || pt->y < gbox.ymin ) { seg1 = seg3; continue; } /* Outside of horizontal range, and not between end points we also skip */ if ( (pt->x > gbox.xmax || pt->x < gbox.xmin) && (pt->y > FP_MAX(seg1->y, seg3->y) || pt->y < FP_MIN(seg1->y, seg3->y)) ) { seg1 = seg3; continue; } side = lw_arc_side(seg1, seg2, seg3, pt); /* On the boundary */ if ( (side == 0) && lw_pt_in_arc(pt, seg1, seg2, seg3) ) { return LW_BOUNDARY; } /* Going "up"! Point to left of arc. */ if ( side < 0 && (seg1->y <= pt->y) && (pt->y < seg3->y) ) { wn++; } /* Going "down"! */ if ( side > 0 && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } /* Inside the arc! */ if ( pt->x <= gbox.xmax && pt->x >= gbox.xmin ) { POINT2D C; double radius = lw_arc_center(seg1, seg2, seg3, &C); double d = distance2d_pt_pt(pt, &C); /* On the boundary! */ if ( d == radius ) return LW_BOUNDARY; /* Within the arc! */ if ( d < radius ) { /* Left side, increment winding number */ if ( side < 0 ) wn++; /* Right side, decrement winding number */ if ( side > 0 ) wn--; } } seg1 = seg3; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return LW_OUTSIDE; } /* Inside */ return LW_INSIDE; } /** * Returns the area in cartesian units. Area is negative if ring is oriented CCW, * positive if it is oriented CW and zero if the ring is degenerate or flat. * http://en.wikipedia.org/wiki/Shoelace_formula */ double ptarray_signed_area(const POINTARRAY *pa) { const POINT2D *P1; const POINT2D *P2; const POINT2D *P3; double sum = 0.0; double x0, x, y1, y2; uint32_t i; if (! pa || pa->npoints < 3 ) return 0.0; P1 = getPoint2d_cp(pa, 0); P2 = getPoint2d_cp(pa, 1); x0 = P1->x; for ( i = 2; i < pa->npoints; i++ ) { P3 = getPoint2d_cp(pa, i); x = P2->x - x0; y1 = P3->y; y2 = P1->y; sum += x * (y2-y1); /* Move forwards! */ P1 = P2; P2 = P3; } return sum / 2.0; } int ptarray_isccw(const POINTARRAY *pa) { double area = 0; area = ptarray_signed_area(pa); if ( area > 0 ) return LW_FALSE; else return LW_TRUE; } POINTARRAY* ptarray_force_dims(const POINTARRAY *pa, int hasz, int hasm) { /* TODO handle zero-length point arrays */ uint32_t i; int in_hasz = FLAGS_GET_Z(pa->flags); int in_hasm = FLAGS_GET_M(pa->flags); POINT4D pt; POINTARRAY *pa_out = ptarray_construct_empty(hasz, hasm, pa->npoints); for( i = 0; i < pa->npoints; i++ ) { getPoint4d_p(pa, i, &pt); if( hasz && ! in_hasz ) pt.z = 0.0; if( hasm && ! in_hasm ) pt.m = 0.0; ptarray_append_point(pa_out, &pt, LW_TRUE); } return pa_out; } POINTARRAY * ptarray_substring(POINTARRAY *ipa, double from, double to, double tolerance) { POINTARRAY *dpa; POINT4D pt; POINT4D p1, p2; POINT4D *p1ptr=&p1; /* don't break strict-aliasing rule */ POINT4D *p2ptr=&p2; int nsegs, i; double length, slength, tlength; int state = 0; /* 0=before, 1=inside */ /* * Create a dynamic pointarray with an initial capacity * equal to full copy of input points */ dpa = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), ipa->npoints); /* Compute total line length */ length = ptarray_length_2d(ipa); LWDEBUGF(3, "Total length: %g", length); /* Get 'from' and 'to' lengths */ from = length*from; to = length*to; LWDEBUGF(3, "From/To: %g/%g", from, to); tlength = 0; getPoint4d_p(ipa, 0, &p1); nsegs = ipa->npoints - 1; for ( i = 0; i < nsegs; i++ ) { double dseg; getPoint4d_p(ipa, i+1, &p2); LWDEBUGF(3 ,"Segment %d: (%g,%g,%g,%g)-(%g,%g,%g,%g)", i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m); /* Find the length of this segment */ slength = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr); /* * We are before requested start. */ if ( state == 0 ) /* before */ { LWDEBUG(3, " Before start"); if ( fabs ( from - ( tlength + slength ) ) <= tolerance ) { LWDEBUG(3, " Second point is our start"); /* * Second point is our start */ ptarray_append_point(dpa, &p2, LW_FALSE); state=1; /* we're inside now */ goto END; } else if ( fabs(from - tlength) <= tolerance ) { LWDEBUG(3, " First point is our start"); /* * First point is our start */ ptarray_append_point(dpa, &p1, LW_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } /* * Didn't reach the 'from' point, * nothing to do */ else if ( from > tlength + slength ) goto END; else /* tlength < from < tlength+slength */ { LWDEBUG(3, " Seg contains first point"); /* * Our start is between first and * second point */ dseg = (from - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); ptarray_append_point(dpa, &pt, LW_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } } if ( state == 1 ) /* inside */ { LWDEBUG(3, " Inside"); /* * 'to' point is our second point. */ if ( fabs(to - ( tlength + slength ) ) <= tolerance ) { LWDEBUG(3, " Second point is our end"); ptarray_append_point(dpa, &p2, LW_FALSE); break; /* substring complete */ } /* * 'to' point is our first point. * (should only happen if 'to' is 0) */ else if ( fabs(to - tlength) <= tolerance ) { LWDEBUG(3, " First point is our end"); ptarray_append_point(dpa, &p1, LW_FALSE); break; /* substring complete */ } /* * Didn't reach the 'end' point, * just copy second point */ else if ( to > tlength + slength ) { ptarray_append_point(dpa, &p2, LW_FALSE); goto END; } /* * 'to' point falls on this segment * Interpolate and break. */ else if ( to < tlength + slength ) { LWDEBUG(3, " Seg contains our end"); dseg = (to - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); ptarray_append_point(dpa, &pt, LW_FALSE); break; } else { LWDEBUG(3, "Unhandled case"); } } END: tlength += slength; memcpy(&p1, &p2, sizeof(POINT4D)); } LWDEBUGF(3, "Out of loop, ptarray has %d points", dpa->npoints); return dpa; } /* * Write into the *ret argument coordinates of the closes point on * the given segment to the reference input point. */ void closest_point_on_segment(const POINT4D *p, const POINT4D *A, const POINT4D *B, POINT4D *ret) { double r; if ( FP_EQUALS(A->x, B->x) && FP_EQUALS(A->y, B->y) ) { *ret = *A; return; } /* * We use comp.graphics.algorithms Frequently Asked Questions method * * (1) AC dot AB * r = ---------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); if (r<0) { *ret = *A; return; } if (r>1) { *ret = *B; return; } ret->x = A->x + ( (B->x - A->x) * r ); ret->y = A->y + ( (B->y - A->y) * r ); ret->z = A->z + ( (B->z - A->z) * r ); ret->m = A->m + ( (B->m - A->m) * r ); } /* * Given a point, returns the location of closest point on pointarray * and, optionally, it's actual distance from the point array. */ double ptarray_locate_point(const POINTARRAY *pa, const POINT4D *p4d, double *mindistout, POINT4D *proj4d) { double mindist=DBL_MAX; double tlen, plen; uint32_t t, seg=0; POINT4D start4d, end4d, projtmp; POINT2D proj, p; const POINT2D *start = NULL, *end = NULL; /* Initialize our 2D copy of the input parameter */ p.x = p4d->x; p.y = p4d->y; if ( ! proj4d ) proj4d = &projtmp; /* Check for special cases (length 0 and 1) */ if ( pa->npoints <= 1 ) { if ( pa->npoints == 1 ) { getPoint4d_p(pa, 0, proj4d); if ( mindistout ) *mindistout = distance2d_pt_pt(&p, getPoint2d_cp(pa, 0)); } return 0.0; } start = getPoint2d_cp(pa, 0); /* Loop through pointarray looking for nearest segment */ for (t=1; tnpoints; t++) { double dist_sqr; end = getPoint2d_cp(pa, t); dist_sqr = distance2d_sqr_pt_seg(&p, start, end); if (dist_sqr < mindist) { mindist = dist_sqr; seg=t-1; if ( mindist == 0 ) { LWDEBUG(3, "Breaking on mindist=0"); break; } } start = end; } mindist = sqrt(mindist); if ( mindistout ) *mindistout = mindist; LWDEBUGF(3, "Closest segment: %d", seg); LWDEBUGF(3, "mindist: %g", mindist); /* * We need to project the * point on the closest segment. */ getPoint4d_p(pa, seg, &start4d); getPoint4d_p(pa, seg+1, &end4d); closest_point_on_segment(p4d, &start4d, &end4d, proj4d); /* Copy 4D values into 2D holder */ proj.x = proj4d->x; proj.y = proj4d->y; LWDEBUGF(3, "Closest segment:%d, npoints:%d", seg, pa->npoints); /* For robustness, force 1 when closest point == endpoint */ if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, end) ) { return 1.0; } LWDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y); tlen = ptarray_length_2d(pa); LWDEBUGF(3, "tlen %g", tlen); /* Location of any point on a zero-length line is 0 */ /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */ if ( tlen == 0 ) return 0; plen=0; start = getPoint2d_cp(pa, 0); for (t=0; t 180 becomes X - 360 */ void ptarray_longitude_shift(POINTARRAY *pa) { uint32_t i; double x; for (i=0; inpoints; i++) { memcpy(&x, getPoint_internal(pa, i), sizeof(double)); if ( x < 0 ) x+= 360; else if ( x > 180 ) x -= 360; memcpy(getPoint_internal(pa, i), &x, sizeof(double)); } } /* * Returns a POINTARRAY with consecutive equal points * removed. Equality test on all dimensions of input. * * Always returns a newly allocated object. */ static POINTARRAY * ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints) { POINTARRAY *out = ptarray_clone_deep(in); ptarray_remove_repeated_points_in_place(out, tolerance, minpoints); return out; } POINTARRAY * ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance) { return ptarray_remove_repeated_points_minpoints(in, tolerance, 2); } void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points) { uint32_t i; double tolsq = tolerance * tolerance; const POINT2D *last = NULL; const POINT2D *pt; uint32_t n_points = pa->npoints; uint32_t n_points_out = 1; size_t pt_size = ptarray_point_size(pa); double dsq = FLT_MAX; /* No-op on short inputs */ if ( n_points <= min_points ) return; last = getPoint2d_cp(pa, 0); void *p_to = ((char *)last) + pt_size; for (i = 1; i < n_points; i++) { int last_point = (i == n_points - 1); /* Look straight into the abyss */ pt = getPoint2d_cp(pa, i); /* Don't drop points if we are running short of points */ if (n_points + n_points_out > min_points + i) { if (tolerance > 0.0) { /* Only drop points that are within our tolerance */ dsq = distance2d_sqr_pt_pt(last, pt); /* Allow any point but the last one to be dropped */ if (!last_point && dsq <= tolsq) { continue; } } else { /* At tolerance zero, only skip exact dupes */ if (memcmp((char*)pt, (char*)last, pt_size) == 0) continue; } /* Got to last point, and it's not very different from */ /* the point that preceded it. We want to keep the last */ /* point, not the second-to-last one, so we pull our write */ /* index back one value */ if (last_point && n_points_out > 1 && tolerance > 0.0 && dsq <= tolsq) { n_points_out--; char *cp = (char *) p_to; cp -= pt_size; p_to = (void *) cp; } } /* Compact all remaining values to front of array */ memcpy(p_to, pt, pt_size); n_points_out++; char *cp = (char *) p_to; cp += pt_size; p_to = (void *) cp; last = pt; } /* Adjust array length */ pa->npoints = n_points_out; return; } /* Out of the points in pa [itfist .. itlast], finds the one that's farthest away from * the segment determined by pts[itfist] and pts[itlast]. * Returns itfirst if no point was found futher away than max_distance_sqr */ static uint32_t ptarray_dp_findsplit_in_place(const POINTARRAY *pts, uint32_t it_first, uint32_t it_last, double max_distance_sqr) { uint32_t split = it_first; if ((it_first - it_last) < 2) return it_first; const POINT2D *A = getPoint2d_cp(pts, it_first); const POINT2D *B = getPoint2d_cp(pts, it_last); if (distance2d_sqr_pt_pt(A, B) < DBL_EPSILON) { /* If p1 == p2, we can just calculate the distance from each point to A */ for (uint32_t itk = it_first + 1; itk < it_last; itk++) { const POINT2D *pk = getPoint2d_cp(pts, itk); double distance_sqr = distance2d_sqr_pt_pt(pk, A); if (distance_sqr > max_distance_sqr) { split = itk; max_distance_sqr = distance_sqr; } } return split; } /* This is based on distance2d_sqr_pt_seg, but heavily inlined here to avoid recalculations */ double ba_x = (B->x - A->x); double ba_y = (B->y - A->y); double ab_length_sqr = (ba_x * ba_x + ba_y * ba_y); /* To avoid the division by ab_length_sqr in the 3rd path, we normalize here * and multiply in the first two paths [(dot_ac_ab < 0) and (> ab_length_sqr)] */ max_distance_sqr *= ab_length_sqr; for (uint32_t itk = it_first + 1; itk < it_last; itk++) { const POINT2D *C = getPoint2d_cp(pts, itk); double distance_sqr; double ca_x = (C->x - A->x); double ca_y = (C->y - A->y); double dot_ac_ab = (ca_x * ba_x + ca_y * ba_y); if (dot_ac_ab <= 0.0) { distance_sqr = distance2d_sqr_pt_pt(C, A) * ab_length_sqr; } else if (dot_ac_ab >= ab_length_sqr) { distance_sqr = distance2d_sqr_pt_pt(C, B) * ab_length_sqr; } else { double s_numerator = ca_x * ba_y - ca_y * ba_x; distance_sqr = s_numerator * s_numerator; /* Missing division by ab_length_sqr on purpose */ } if (distance_sqr > max_distance_sqr) { split = itk; max_distance_sqr = distance_sqr; } } return split; } void ptarray_simplify_in_place(POINTARRAY *pa, double tolerance, uint32_t minpts) { /* Do not try to simplify really short things */ if (pa->npoints < 3 || pa->npoints <= minpts) return; /* We use this array to keep track of the points we are keeping, so * we store just TRUE / FALSE in their position */ uint8_t *kept_points = lwalloc(sizeof(uint8_t) * pa->npoints); memset(kept_points, LW_FALSE, sizeof(uint8_t) * pa->npoints); kept_points[0] = LW_TRUE; kept_points[pa->npoints - 1] = LW_TRUE; uint32_t keptn = 2; /* We use this array as a stack to store the iterators that we are going to need * in the following steps. * This is ~10% faster than iterating over @kept_points looking for them */ uint32_t *iterator_stack = lwalloc(sizeof(uint32_t) * pa->npoints); iterator_stack[0] = 0; uint32_t iterator_stack_size = 1; uint32_t it_first = 0; uint32_t it_last = pa->npoints - 1; const double tolerance_sqr = tolerance * tolerance; /* For the first @minpts points we ignore the tolerance */ double it_tol = keptn >= minpts ? tolerance_sqr : -1.0; while (iterator_stack_size) { uint32_t split = ptarray_dp_findsplit_in_place(pa, it_first, it_last, it_tol); if (split == it_first) { it_first = it_last; it_last = iterator_stack[--iterator_stack_size]; } else { kept_points[split] = LW_TRUE; keptn++; iterator_stack[iterator_stack_size++] = it_last; it_last = split; it_tol = keptn >= minpts ? tolerance_sqr : -1.0; } } const size_t pt_size = ptarray_point_size(pa); /* The first point is already in place, so we don't need to copy it */ size_t kept_it = 1; if (keptn == 2) { /* If there are 2 points remaining, it has to be first and last as * we added those at the start */ memcpy(pa->serialized_pointlist + pt_size * kept_it, pa->serialized_pointlist + pt_size * (pa->npoints - 1), pt_size); } else { for (uint32_t i = 1; i < pa->npoints; i++) { if (kept_points[i]) { memcpy(pa->serialized_pointlist + pt_size * kept_it, pa->serialized_pointlist + pt_size * i, pt_size); kept_it++; } } } pa->npoints = keptn; lwfree(kept_points); lwfree(iterator_stack); } /************************************************************************/ /** * Find the 2d length of the given #POINTARRAY, using circular * arc interpolation between each coordinate triple. * Length(A1, A2, A3, A4, A5) = Length(A1, A2, A3)+Length(A3, A4, A5) */ double ptarray_arc_length_2d(const POINTARRAY *pts) { double dist = 0.0; uint32_t i; const POINT2D *a1; const POINT2D *a2; const POINT2D *a3; if ( pts->npoints % 2 != 1 ) lwerror("arc point array with even number of points"); a1 = getPoint2d_cp(pts, 0); for ( i=2; i < pts->npoints; i += 2 ) { a2 = getPoint2d_cp(pts, i-1); a3 = getPoint2d_cp(pts, i); dist += lw_arc_length(a1, a2, a3); a1 = a3; } return dist; } /** * Find the 2d length of the given #POINTARRAY (even if it's 3d) */ double ptarray_length_2d(const POINTARRAY *pts) { double dist = 0.0; uint32_t i; const POINT2D *frm; const POINT2D *to; if ( pts->npoints < 2 ) return 0.0; frm = getPoint2d_cp(pts, 0); for ( i=1; i < pts->npoints; i++ ) { to = getPoint2d_cp(pts, i); dist += sqrt( ((frm->x - to->x)*(frm->x - to->x)) + ((frm->y - to->y)*(frm->y - to->y)) ); frm = to; } return dist; } /** * Find the 3d/2d length of the given #POINTARRAY * (depending on its dimensionality) */ double ptarray_length(const POINTARRAY *pts) { double dist = 0.0; uint32_t i; POINT3DZ frm; POINT3DZ to; if ( pts->npoints < 2 ) return 0.0; /* compute 2d length if 3d is not available */ if ( ! FLAGS_GET_Z(pts->flags) ) return ptarray_length_2d(pts); getPoint3dz_p(pts, 0, &frm); for ( i=1; i < pts->npoints; i++ ) { getPoint3dz_p(pts, i, &to); dist += sqrt( ((frm.x - to.x)*(frm.x - to.x)) + ((frm.y - to.y)*(frm.y - to.y)) + ((frm.z - to.z)*(frm.z - to.z)) ); frm = to; } return dist; } /** * Affine transform a pointarray. */ void ptarray_affine(POINTARRAY *pa, const AFFINE *a) { uint32_t i; double x,y,z; POINT4D p4d; LWDEBUG(2, "lwgeom_affine_ptarray start"); if ( FLAGS_GET_Z(pa->flags) ) { LWDEBUG(3, " has z"); for (i=0; inpoints; i++) { getPoint4d_p(pa, i, &p4d); x = p4d.x; y = p4d.y; z = p4d.z; p4d.x = a->afac * x + a->bfac * y + a->cfac * z + a->xoff; p4d.y = a->dfac * x + a->efac * y + a->ffac * z + a->yoff; p4d.z = a->gfac * x + a->hfac * y + a->ifac * z + a->zoff; ptarray_set_point4d(pa, i, &p4d); LWDEBUGF(3, " POINT %g %g %g => %g %g %g", x, y, z, p4d.x, p4d.y, p4d.z); } } else { LWDEBUG(3, " doesn't have z"); for (i=0; inpoints; i++) { getPoint4d_p(pa, i, &p4d); x = p4d.x; y = p4d.y; p4d.x = a->afac * x + a->bfac * y + a->xoff; p4d.y = a->dfac * x + a->efac * y + a->yoff; ptarray_set_point4d(pa, i, &p4d); LWDEBUGF(3, " POINT %g %g => %g %g", x, y, p4d.x, p4d.y); } } LWDEBUG(3, "lwgeom_affine_ptarray end"); } /** * WARNING, make sure you send in only 16-member double arrays * or obviously things will go pear-shaped fast. */ #if 0 static int gluInvertMatrix(const double *m, double *invOut) { double inv[16], det; int i; inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; if (det == 0) return LW_FALSE; det = 1.0 / det; for (i = 0; i < 16; i++) invOut[i] = inv[i] * det; return LW_TRUE; } #endif /** * Scale a pointarray. */ void ptarray_scale(POINTARRAY *pa, const POINT4D *fact) { uint32_t i; POINT4D p4d; LWDEBUG(3, "ptarray_scale start"); for (i=0; inpoints; i++) { getPoint4d_p(pa, i, &p4d); p4d.x *= fact->x; p4d.y *= fact->y; p4d.z *= fact->z; p4d.m *= fact->m; ptarray_set_point4d(pa, i, &p4d); } LWDEBUG(3, "ptarray_scale end"); } int ptarray_startpoint(const POINTARRAY *pa, POINT4D *pt) { return getPoint4d_p(pa, 0, pt); } /* * Stick an array of points to the given gridspec. * Return "gridded" points in *outpts and their number in *outptsn. * * Two consecutive points falling on the same grid cell are collapsed * into one single point. * */ void ptarray_grid_in_place(POINTARRAY *pa, const gridspec *grid) { uint32_t i, j = 0; POINT4D *p, *p_out = NULL; int ndims = FLAGS_NDIMS(pa->flags); int has_z = FLAGS_GET_Z(pa->flags); int has_m = FLAGS_GET_M(pa->flags); LWDEBUGF(2, "%s called on %p", __func__, pa); for (i = 0; i < pa->npoints; i++) { /* Look straight into the abyss */ p = (POINT4D*)(getPoint_internal(pa, i)); if (grid->xsize > 0) { p->x = rint((p->x - grid->ipx)/grid->xsize) * grid->xsize + grid->ipx; } if (grid->ysize > 0) { p->y = rint((p->y - grid->ipy)/grid->ysize) * grid->ysize + grid->ipy; } /* Read and round this point */ /* Z is always in third position */ if (has_z) { if (grid->zsize > 0) p->z = rint((p->z - grid->ipz)/grid->zsize) * grid->zsize + grid->ipz; } /* M might be in 3rd or 4th position */ if (has_m) { /* In POINT M, M is in 3rd position */ if (grid->msize > 0 && !has_z) p->z = rint((p->z - grid->ipm)/grid->msize) * grid->msize + grid->ipm; /* In POINT ZM, M is in 4th position */ if (grid->msize > 0 && has_z) p->m = rint((p->m - grid->ipm)/grid->msize) * grid->msize + grid->ipm; } /* Skip duplicates */ if ( p_out && FP_EQUALS(p_out->x, p->x) && FP_EQUALS(p_out->y, p->y) && (ndims > 2 ? FP_EQUALS(p_out->z, p->z) : 1) && (ndims > 3 ? FP_EQUALS(p_out->m, p->m) : 1) ) { continue; } /* Write rounded values into the next available point */ p_out = (POINT4D*)(getPoint_internal(pa, j++)); p_out->x = p->x; p_out->y = p->y; if (ndims > 2) p_out->z = p->z; if (ndims > 3) p_out->m = p->m; } /* Update output ptarray length */ pa->npoints = j; return; } int ptarray_npoints_in_rect(const POINTARRAY *pa, const GBOX *gbox) { const POINT2D *pt; int n = 0; uint32_t i; for ( i = 0; i < pa->npoints; i++ ) { pt = getPoint2d_cp(pa, i); if ( gbox_contains_point2d(gbox, pt) ) n++; } return n; } lwgeom/src/liblwgeom/lwmline.c0000644000176200001440000000652213773172540016145 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" void lwmline_release(LWMLINE *lwmline) { lwgeom_release(lwmline_as_lwgeom(lwmline)); } LWMLINE * lwmline_construct_empty(int32_t srid, char hasz, char hasm) { LWMLINE *ret = (LWMLINE*)lwcollection_construct_empty(MULTILINETYPE, srid, hasz, hasm); return ret; } LWMLINE* lwmline_add_lwline(LWMLINE *mobj, const LWLINE *obj) { return (LWMLINE*)lwcollection_add_lwgeom((LWCOLLECTION*)mobj, (LWGEOM*)obj); } /** * Re-write the measure ordinate (or add one, if it isn't already there) interpolating * the measure between the supplied start and end values. */ LWMLINE* lwmline_measured_from_lwmline(const LWMLINE *lwmline, double m_start, double m_end) { uint32_t i = 0; int hasm = 0, hasz = 0; double length = 0.0, length_so_far = 0.0; double m_range = m_end - m_start; LWGEOM **geoms = NULL; if ( lwmline->type != MULTILINETYPE ) { lwerror("lwmline_measured_from_lmwline: only multiline types supported"); return NULL; } hasz = FLAGS_GET_Z(lwmline->flags); hasm = 1; /* Calculate the total length of the mline */ for ( i = 0; i < lwmline->ngeoms; i++ ) { LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; if ( lwline->points && lwline->points->npoints > 1 ) { length += ptarray_length_2d(lwline->points); } } if ( lwgeom_is_empty((LWGEOM*)lwmline) ) { return (LWMLINE*)lwcollection_construct_empty(MULTILINETYPE, lwmline->srid, hasz, hasm); } geoms = lwalloc(sizeof(LWGEOM*) * lwmline->ngeoms); for ( i = 0; i < lwmline->ngeoms; i++ ) { double sub_m_start, sub_m_end; double sub_length = 0.0; LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; if ( lwline->points && lwline->points->npoints > 1 ) { sub_length = ptarray_length_2d(lwline->points); } sub_m_start = (m_start + m_range * length_so_far / length); sub_m_end = (m_start + m_range * (length_so_far + sub_length) / length); geoms[i] = (LWGEOM*)lwline_measured_from_lwline(lwline, sub_m_start, sub_m_end); length_so_far += sub_length; } return (LWMLINE*)lwcollection_construct(lwmline->type, lwmline->srid, NULL, lwmline->ngeoms, geoms); } void lwmline_free(LWMLINE *mline) { if (!mline) return; if (mline->bbox) lwfree(mline->bbox); if (mline->geoms) { for (uint32_t i = 0; i < mline->ngeoms; i++) if (mline->geoms[i]) lwline_free(mline->geoms[i]); lwfree(mline->geoms); } lwfree(mline); } lwgeom/src/liblwgeom/lwcircstring.c0000644000176200001440000001726713773172540017220 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic LWCIRCSTRING functions */ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" void printLWCIRCSTRING(LWCIRCSTRING *curve); void lwcircstring_release(LWCIRCSTRING *lwcirc); char lwcircstring_same(const LWCIRCSTRING *me, const LWCIRCSTRING *you); LWCIRCSTRING *lwcircstring_from_lwpointarray(int32_t srid, uint32_t npoints, LWPOINT **points); LWCIRCSTRING *lwcircstring_from_lwmpoint(int32_t srid, LWMPOINT *mpoint); LWCIRCSTRING *lwcircstring_addpoint(LWCIRCSTRING *curve, LWPOINT *point, uint32_t where); LWCIRCSTRING *lwcircstring_removepoint(LWCIRCSTRING *curve, uint32_t index); void lwcircstring_setPoint4d(LWCIRCSTRING *curve, uint32_t index, POINT4D *newpoint); /* * Construct a new LWCIRCSTRING. points will *NOT* be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWCIRCSTRING * lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points) { LWCIRCSTRING *result; /* * The first arc requires three points. Each additional * arc requires two more points. Thus the minimum point count * is three, and the count must be odd. */ if (points->npoints % 2 != 1 || points->npoints < 3) { lwnotice("lwcircstring_construct: invalid point count %d", points->npoints); } result = (LWCIRCSTRING*) lwalloc(sizeof(LWCIRCSTRING)); result->type = CIRCSTRINGTYPE; result->flags = points->flags; FLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->points = points; result->bbox = bbox; return result; } LWCIRCSTRING * lwcircstring_construct_empty(int32_t srid, char hasz, char hasm) { LWCIRCSTRING *result = lwalloc(sizeof(LWCIRCSTRING)); result->type = CIRCSTRINGTYPE; result->flags = lwflags(hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(hasz, hasm, 1); result->bbox = NULL; return result; } void lwcircstring_release(LWCIRCSTRING *lwcirc) { lwgeom_release(lwcircstring_as_lwgeom(lwcirc)); } void lwcircstring_free(LWCIRCSTRING *curve) { if ( ! curve ) return; if ( curve->bbox ) lwfree(curve->bbox); if ( curve->points ) ptarray_free(curve->points); lwfree(curve); } void printLWCIRCSTRING(LWCIRCSTRING *curve) { lwnotice("LWCIRCSTRING {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(curve->flags)); lwnotice(" srid = %i", (int)curve->srid); printPA(curve->points); lwnotice("}"); } /* @brief Clone LWCIRCSTRING object. Serialized point lists are not copied. * * @see ptarray_clone */ LWCIRCSTRING * lwcircstring_clone(const LWCIRCSTRING *g) { return (LWCIRCSTRING *)lwline_clone((LWLINE *)g); } /* check coordinate equality */ char lwcircstring_same(const LWCIRCSTRING *me, const LWCIRCSTRING *you) { return ptarray_same(me->points, you->points); } /* * Construct a LWCIRCSTRING from an array of LWPOINTs * LWCIRCSTRING dimensions are large enough to host all input dimensions. */ LWCIRCSTRING * lwcircstring_from_lwpointarray(int32_t srid, uint32_t npoints, LWPOINT **points) { int zmflag=0; uint32_t i; POINTARRAY *pa; uint8_t *newpoints, *ptr; size_t ptsize, size; /* * Find output dimensions, check integrity */ for (i = 0; i < npoints; i++) { if (points[i]->type != POINTTYPE) { lwerror("lwcurve_from_lwpointarray: invalid input type: %s", lwtype_name(points[i]->type)); return NULL; } if (FLAGS_GET_Z(points[i]->flags)) zmflag |= 2; if (FLAGS_GET_M(points[i]->flags)) zmflag |= 1; if (zmflag == 3) break; } if (zmflag == 0) ptsize = 2 * sizeof(double); else if (zmflag == 3) ptsize = 4 * sizeof(double); else ptsize = 3 * sizeof(double); /* * Allocate output points array */ size = ptsize * npoints; newpoints = lwalloc(size); memset(newpoints, 0, size); ptr = newpoints; for (i = 0; i < npoints; i++) { size = ptarray_point_size(points[i]->point); memcpy(ptr, getPoint_internal(points[i]->point, 0), size); ptr += ptsize; } pa = ptarray_construct_reference_data(zmflag&2, zmflag&1, npoints, newpoints); return lwcircstring_construct(srid, NULL, pa); } /* * Construct a LWCIRCSTRING from a LWMPOINT */ LWCIRCSTRING * lwcircstring_from_lwmpoint(int32_t srid, LWMPOINT *mpoint) { uint32_t i; POINTARRAY *pa; char zmflag = FLAGS_GET_ZM(mpoint->flags); size_t ptsize, size; uint8_t *newpoints, *ptr; if (zmflag == 0) ptsize = 2 * sizeof(double); else if (zmflag == 3) ptsize = 4 * sizeof(double); else ptsize = 3 * sizeof(double); /* Allocate space for output points */ size = ptsize * mpoint->ngeoms; newpoints = lwalloc(size); memset(newpoints, 0, size); ptr = newpoints; for (i = 0; i < mpoint->ngeoms; i++) { memcpy(ptr, getPoint_internal(mpoint->geoms[i]->point, 0), ptsize); ptr += ptsize; } pa = ptarray_construct_reference_data(zmflag&2, zmflag&1, mpoint->ngeoms, newpoints); LWDEBUGF(3, "lwcurve_from_lwmpoint: constructed pointarray for %d points, %d zmflag", mpoint->ngeoms, zmflag); return lwcircstring_construct(srid, NULL, pa); } LWCIRCSTRING * lwcircstring_addpoint(LWCIRCSTRING *curve, LWPOINT *point, uint32_t where) { POINTARRAY *newpa; LWCIRCSTRING *ret; newpa = ptarray_addPoint(curve->points, getPoint_internal(point->point, 0), FLAGS_NDIMS(point->flags), where); ret = lwcircstring_construct(curve->srid, NULL, newpa); return ret; } LWCIRCSTRING * lwcircstring_removepoint(LWCIRCSTRING *curve, uint32_t index) { POINTARRAY *newpa; LWCIRCSTRING *ret; newpa = ptarray_removePoint(curve->points, index); ret = lwcircstring_construct(curve->srid, NULL, newpa); return ret; } /* * Note: input will be changed, make sure you have permissions for this. * */ void lwcircstring_setPoint4d(LWCIRCSTRING *curve, uint32_t index, POINT4D *newpoint) { ptarray_set_point4d(curve->points, index, newpoint); } int lwcircstring_is_closed(const LWCIRCSTRING *curve) { if (lwgeom_has_z((LWGEOM*)curve)) return ptarray_is_closed_3d(curve->points); return ptarray_is_closed_2d(curve->points); } double lwcircstring_length(const LWCIRCSTRING *circ) { return lwcircstring_length_2d(circ); } double lwcircstring_length_2d(const LWCIRCSTRING *circ) { if ( lwcircstring_is_empty(circ) ) return 0.0; return ptarray_arc_length_2d(circ->points); } /* * Returns freshly allocated #LWPOINT that corresponds to the index where. * Returns NULL if the geometry is empty or the index invalid. */ LWPOINT* lwcircstring_get_lwpoint(const LWCIRCSTRING *circ, uint32_t where) { POINT4D pt; LWPOINT *lwpoint; POINTARRAY *pa; if ( lwcircstring_is_empty(circ) || where >= circ->points->npoints ) return NULL; pa = ptarray_construct_empty(FLAGS_GET_Z(circ->flags), FLAGS_GET_M(circ->flags), 1); pt = getPoint4d(circ->points, where); ptarray_append_point(pa, &pt, LW_TRUE); lwpoint = lwpoint_construct(circ->srid, NULL, pa); return lwpoint; } lwgeom/src/liblwgeom/lwout_twkb.h0000644000176200001440000000757713773172540016717 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2013 Nicklas Avén * **********************************************************************/ /********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * Copyright 2013 Nicklas Avén * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include #include "bytebuffer.h" /* Maximum number of geometry dimmensions that internal arrays can hold */ #define MAX_N_DIMS 4 #define MAX_BBOX_SIZE 64 #define MAX_SIZE_SIZE 8 /** * Header true/false flags */ #define FIRST_BYTE_SET_BBOXES(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) #define FIRST_BYTE_SET_SIZES(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) #define FIRST_BYTE_SET_IDLIST(flag, bool) ((flag) = ((bool) ? (flag) | 0x04 : (flag) & (~0x04))) #define FIRST_BYTE_SET_EXTENDED(flag, bool) ((flag) = ((bool) ? (flag) | 0x08 : (flag) & (~0x08))) #define FIRST_BYTE_SET_EMPTY(flag, bool) ((flag) = ((bool) ? (flag) | 0x10 : (flag) & (~0x10))) /** * Macros for manipulating the 'type_precision' int. An int8_t used as follows: * Type 4 bits * Precision 4 bits */ #define TYPE_PREC_SET_TYPE(flag, type) ((flag) = ((flag) & 0xF0) | (((type) & 0x0F))) #define TYPE_PREC_SET_PREC(flag, prec) ((flag) = ((flag) & 0x0F) | (((prec) & 0x0F) << 4)) #define HIGHER_DIM_SET_HASZ(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) #define HIGHER_DIM_SET_HASM(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) #define HIGHER_DIM_SET_PRECZ(flag, prec) ((flag) = ((flag) & 0xE3) | (((prec) & 0x07) << 2)) #define HIGHER_DIM_SET_PRECM(flag, prec) ((flag) = ((flag) & 0x1F) | (((prec) & 0x07) << 5)) typedef struct { /* Options defined at start */ uint8_t variant; int8_t prec_xy; int8_t prec_z; int8_t prec_m; float factor[4]; /*What factor to multiply the coordiinates with to get the requested precision*/ } TWKB_GLOBALS; typedef struct { uint8_t variant; /*options that change at runtime*/ bytebuffer_t *header_buf; bytebuffer_t *geom_buf; int hasz; int hasm; const int64_t *idlist; int64_t bbox_min[MAX_N_DIMS]; int64_t bbox_max[MAX_N_DIMS]; int64_t accum_rels[MAX_N_DIMS]; /*Holds the acculmulated relative values*/ } TWKB_STATE; static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int lwpoint_to_twkb_buf(const LWPOINT *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *parent_state); lwgeom/src/liblwgeom/gbox.c0000644000176200001440000005613113773172540015436 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeodetic.h" #include "lwgeom_log.h" #include #include GBOX* gbox_new(lwflags_t flags) { GBOX *g = (GBOX*)lwalloc(sizeof(GBOX)); gbox_init(g); g->flags = flags; return g; } void gbox_init(GBOX *gbox) { memset(gbox, 0, sizeof(GBOX)); } GBOX* gbox_clone(const GBOX *gbox) { GBOX *g = lwalloc(sizeof(GBOX)); memcpy(g, gbox, sizeof(GBOX)); return g; } /* TODO to be removed */ BOX3D* box3d_from_gbox(const GBOX *gbox) { BOX3D *b; assert(gbox); b = lwalloc(sizeof(BOX3D)); b->xmin = gbox->xmin; b->xmax = gbox->xmax; b->ymin = gbox->ymin; b->ymax = gbox->ymax; if ( FLAGS_GET_Z(gbox->flags) ) { b->zmin = gbox->zmin; b->zmax = gbox->zmax; } else { b->zmin = b->zmax = 0.0; } b->srid = SRID_UNKNOWN; return b; } /* TODO to be removed */ GBOX* box3d_to_gbox(const BOX3D *b3d) { GBOX *b; assert(b3d); b = lwalloc(sizeof(GBOX)); b->xmin = b3d->xmin; b->xmax = b3d->xmax; b->ymin = b3d->ymin; b->ymax = b3d->ymax; b->zmin = b3d->zmin; b->zmax = b3d->zmax; return b; } void gbox_expand(GBOX *g, double d) { g->xmin -= d; g->xmax += d; g->ymin -= d; g->ymax += d; if (FLAGS_GET_Z(g->flags) || FLAGS_GET_GEODETIC(g->flags)) { g->zmin -= d; g->zmax += d; } if (FLAGS_GET_M(g->flags)) { g->mmin -= d; g->mmax += d; } } void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm) { g->xmin -= dx; g->xmax += dx; g->ymin -= dy; g->ymax += dy; if (FLAGS_GET_Z(g->flags)) { g->zmin -= dz; g->zmax += dz; } if (FLAGS_GET_M(g->flags)) { g->mmin -= dm; g->mmax += dm; } } int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout) { if ( ( ! g1 ) && ( ! g2 ) ) return LW_FALSE; else if (!g1) { memcpy(gout, g2, sizeof(GBOX)); return LW_TRUE; } else if (!g2) { memcpy(gout, g1, sizeof(GBOX)); return LW_TRUE; } gout->flags = g1->flags; gout->xmin = FP_MIN(g1->xmin, g2->xmin); gout->xmax = FP_MAX(g1->xmax, g2->xmax); gout->ymin = FP_MIN(g1->ymin, g2->ymin); gout->ymax = FP_MAX(g1->ymax, g2->ymax); gout->zmin = FP_MIN(g1->zmin, g2->zmin); gout->zmax = FP_MAX(g1->zmax, g2->zmax); return LW_TRUE; } int gbox_same(const GBOX *g1, const GBOX *g2) { if (FLAGS_GET_ZM(g1->flags) != FLAGS_GET_ZM(g2->flags)) return LW_FALSE; if (!gbox_same_2d(g1, g2)) return LW_FALSE; if (FLAGS_GET_Z(g1->flags) && (g1->zmin != g2->zmin || g1->zmax != g2->zmax)) return LW_FALSE; if (FLAGS_GET_M(g1->flags) && (g1->mmin != g2->mmin || g1->mmax != g2->mmax)) return LW_FALSE; return LW_TRUE; } int gbox_same_2d(const GBOX *g1, const GBOX *g2) { if (g1->xmin == g2->xmin && g1->ymin == g2->ymin && g1->xmax == g2->xmax && g1->ymax == g2->ymax) return LW_TRUE; return LW_FALSE; } int gbox_same_2d_float(const GBOX *g1, const GBOX *g2) { if ((g1->xmax == g2->xmax || next_float_up(g1->xmax) == next_float_up(g2->xmax)) && (g1->ymax == g2->ymax || next_float_up(g1->ymax) == next_float_up(g2->ymax)) && (g1->xmin == g2->xmin || next_float_down(g1->xmin) == next_float_down(g1->xmin)) && (g1->ymin == g2->ymin || next_float_down(g2->ymin) == next_float_down(g2->ymin))) return LW_TRUE; return LW_FALSE; } int gbox_is_valid(const GBOX *gbox) { /* X */ if ( ! isfinite(gbox->xmin) || isnan(gbox->xmin) || ! isfinite(gbox->xmax) || isnan(gbox->xmax) ) return LW_FALSE; /* Y */ if ( ! isfinite(gbox->ymin) || isnan(gbox->ymin) || ! isfinite(gbox->ymax) || isnan(gbox->ymax) ) return LW_FALSE; /* Z */ if ( FLAGS_GET_GEODETIC(gbox->flags) || FLAGS_GET_Z(gbox->flags) ) { if ( ! isfinite(gbox->zmin) || isnan(gbox->zmin) || ! isfinite(gbox->zmax) || isnan(gbox->zmax) ) return LW_FALSE; } /* M */ if ( FLAGS_GET_M(gbox->flags) ) { if ( ! isfinite(gbox->mmin) || isnan(gbox->mmin) || ! isfinite(gbox->mmax) || isnan(gbox->mmax) ) return LW_FALSE; } return LW_TRUE; } int gbox_merge_point3d(const POINT3D *p, GBOX *gbox) { if ( gbox->xmin > p->x ) gbox->xmin = p->x; if ( gbox->ymin > p->y ) gbox->ymin = p->y; if ( gbox->zmin > p->z ) gbox->zmin = p->z; if ( gbox->xmax < p->x ) gbox->xmax = p->x; if ( gbox->ymax < p->y ) gbox->ymax = p->y; if ( gbox->zmax < p->z ) gbox->zmax = p->z; return LW_SUCCESS; } int gbox_init_point3d(const POINT3D *p, GBOX *gbox) { gbox->xmin = gbox->xmax = p->x; gbox->ymin = gbox->ymax = p->y; gbox->zmin = gbox->zmax = p->z; return LW_SUCCESS; } int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt) { if ( gbox->xmin > pt->x || gbox->ymin > pt->y || gbox->zmin > pt->z || gbox->xmax < pt->x || gbox->ymax < pt->y || gbox->zmax < pt->z ) { return LW_FALSE; } return LW_TRUE; } int gbox_merge(const GBOX *new_box, GBOX *merge_box) { assert(merge_box); if ( FLAGS_GET_ZM(merge_box->flags) != FLAGS_GET_ZM(new_box->flags) ) return LW_FAILURE; if ( new_box->xmin < merge_box->xmin) merge_box->xmin = new_box->xmin; if ( new_box->ymin < merge_box->ymin) merge_box->ymin = new_box->ymin; if ( new_box->xmax > merge_box->xmax) merge_box->xmax = new_box->xmax; if ( new_box->ymax > merge_box->ymax) merge_box->ymax = new_box->ymax; if ( FLAGS_GET_Z(merge_box->flags) || FLAGS_GET_GEODETIC(merge_box->flags) ) { if ( new_box->zmin < merge_box->zmin) merge_box->zmin = new_box->zmin; if ( new_box->zmax > merge_box->zmax) merge_box->zmax = new_box->zmax; } if ( FLAGS_GET_M(merge_box->flags) ) { if ( new_box->mmin < merge_box->mmin) merge_box->mmin = new_box->mmin; if ( new_box->mmax > merge_box->mmax) merge_box->mmax = new_box->mmax; } return LW_SUCCESS; } int gbox_overlaps(const GBOX *g1, const GBOX *g2) { /* Make sure our boxes are consistent */ if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) ) lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); /* Check X/Y first */ if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || g1->xmin > g2->xmax || g1->ymin > g2->ymax ) return LW_FALSE; /* Deal with the geodetic case special: we only compare the geodetic boxes (x/y/z) */ /* Never the M dimension */ if ( FLAGS_GET_GEODETIC(g1->flags) && FLAGS_GET_GEODETIC(g2->flags) ) { if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) return LW_FALSE; else return LW_TRUE; } /* If both geodetic or both have Z, check Z */ if ( FLAGS_GET_Z(g1->flags) && FLAGS_GET_Z(g2->flags) ) { if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) return LW_FALSE; } /* If both have M, check M */ if ( FLAGS_GET_M(g1->flags) && FLAGS_GET_M(g2->flags) ) { if ( g1->mmax < g2->mmin || g1->mmin > g2->mmax ) return LW_FALSE; } return LW_TRUE; } int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2) { /* Make sure our boxes are consistent */ if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) ) lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); /* Check X/Y first */ if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || g1->xmin > g2->xmax || g1->ymin > g2->ymax ) return LW_FALSE; return LW_TRUE; } int gbox_contains_2d(const GBOX *g1, const GBOX *g2) { if ( ( g2->xmin < g1->xmin ) || ( g2->xmax > g1->xmax ) || ( g2->ymin < g1->ymin ) || ( g2->ymax > g1->ymax ) ) { return LW_FALSE; } return LW_TRUE; } int gbox_contains_point2d(const GBOX *g, const POINT2D *p) { if ( ( g->xmin <= p->x ) && ( g->xmax >= p->x ) && ( g->ymin <= p->y ) && ( g->ymax >= p->y ) ) { return LW_TRUE; } return LW_FALSE; } /** * Warning, this function is only good for x/y/z boxes, used * in unit testing of geodetic box generation. */ GBOX* gbox_from_string(const char *str) { const char *ptr = str; char *nextptr; char *gbox_start = strstr(str, "GBOX(("); GBOX *gbox = gbox_new(lwflags(0,0,1)); if ( ! gbox_start ) return NULL; /* No header found */ ptr += 6; gbox->xmin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->ymin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->zmin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 3; gbox->xmax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->ymax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->zmax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ return gbox; } char* gbox_to_string(const GBOX *gbox) { const size_t sz = 138; char *str = NULL; if ( ! gbox ) return lwstrdup("NULL POINTER"); str = (char*)lwalloc(sz); if ( FLAGS_GET_GEODETIC(gbox->flags) ) { snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); return str; } if ( FLAGS_GET_Z(gbox->flags) && FLAGS_GET_M(gbox->flags) ) { snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->zmax, gbox->mmax); return str; } if ( FLAGS_GET_Z(gbox->flags) ) { snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); return str; } if ( FLAGS_GET_M(gbox->flags) ) { snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->mmax); return str; } snprintf(str, sz, "GBOX((%.8g,%.8g),(%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->xmax, gbox->ymax); return str; } GBOX* gbox_copy(const GBOX *box) { GBOX *copy = (GBOX*)lwalloc(sizeof(GBOX)); memcpy(copy, box, sizeof(GBOX)); return copy; } void gbox_duplicate(const GBOX *original, GBOX *duplicate) { assert(duplicate); assert(original); memcpy(duplicate, original, sizeof(GBOX)); } size_t gbox_serialized_size(lwflags_t flags) { if (FLAGS_GET_GEODETIC(flags)) return 6 * sizeof(float); else return 2 * FLAGS_NDIMS(flags) * sizeof(float); } /* ******************************************************************************** ** Compute cartesian bounding GBOX boxes from LWGEOM. */ int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox) { POINT2D xmin, ymin, xmax, ymax; POINT2D C; int A2_side; double radius_A; LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called."); radius_A = lw_arc_center(A1, A2, A3, &C); /* Negative radius signals straight line, p1/p2/p3 are collinear */ if (radius_A < 0.0) { gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); return LW_SUCCESS; } /* Matched start/end points imply circle */ if ( A1->x == A3->x && A1->y == A3->y ) { gbox->xmin = C.x - radius_A; gbox->ymin = C.y - radius_A; gbox->xmax = C.x + radius_A; gbox->ymax = C.y + radius_A; return LW_SUCCESS; } /* First approximation, bounds of start/end points */ gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); /* Create points for the possible extrema */ xmin.x = C.x - radius_A; xmin.y = C.y; ymin.x = C.x; ymin.y = C.y - radius_A; xmax.x = C.x + radius_A; xmax.y = C.y; ymax.x = C.x; ymax.y = C.y + radius_A; /* Divide the circle into two parts, one on each side of a line joining p1 and p3. The circle extrema on the same side of that line as p2 is on, are also the extrema of the bbox. */ A2_side = lw_segment_side(A1, A3, A2); if ( A2_side == lw_segment_side(A1, A3, &xmin) ) gbox->xmin = xmin.x; if ( A2_side == lw_segment_side(A1, A3, &ymin) ) gbox->ymin = ymin.y; if ( A2_side == lw_segment_side(A1, A3, &xmax) ) gbox->xmax = xmax.x; if ( A2_side == lw_segment_side(A1, A3, &ymax) ) gbox->ymax = ymax.y; return LW_SUCCESS; } static int lw_arc_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox) { int rv; LWDEBUG(2, "lw_arc_calculate_gbox_cartesian called."); rv = lw_arc_calculate_gbox_cartesian_2d((POINT2D*)p1, (POINT2D*)p2, (POINT2D*)p3, gbox); gbox->zmin = FP_MIN(p1->z, p3->z); gbox->mmin = FP_MIN(p1->m, p3->m); gbox->zmax = FP_MAX(p1->z, p3->z); gbox->mmax = FP_MAX(p1->m, p3->m); return rv; } static void ptarray_calculate_gbox_cartesian_2d(const POINTARRAY *pa, GBOX *gbox) { const POINT2D *p = getPoint2d_cp(pa, 0); gbox->xmax = gbox->xmin = p->x; gbox->ymax = gbox->ymin = p->y; for (uint32_t i = 1; i < pa->npoints; i++) { p = getPoint2d_cp(pa, i); gbox->xmin = FP_MIN(gbox->xmin, p->x); gbox->xmax = FP_MAX(gbox->xmax, p->x); gbox->ymin = FP_MIN(gbox->ymin, p->y); gbox->ymax = FP_MAX(gbox->ymax, p->y); } } /* Works with X/Y/Z. Needs to be adjusted after if X/Y/M was required */ static void ptarray_calculate_gbox_cartesian_3d(const POINTARRAY *pa, GBOX *gbox) { const POINT3D *p = getPoint3d_cp(pa, 0); gbox->xmax = gbox->xmin = p->x; gbox->ymax = gbox->ymin = p->y; gbox->zmax = gbox->zmin = p->z; for (uint32_t i = 1; i < pa->npoints; i++) { p = getPoint3d_cp(pa, i); gbox->xmin = FP_MIN(gbox->xmin, p->x); gbox->xmax = FP_MAX(gbox->xmax, p->x); gbox->ymin = FP_MIN(gbox->ymin, p->y); gbox->ymax = FP_MAX(gbox->ymax, p->y); gbox->zmin = FP_MIN(gbox->zmin, p->z); gbox->zmax = FP_MAX(gbox->zmax, p->z); } } static void ptarray_calculate_gbox_cartesian_4d(const POINTARRAY *pa, GBOX *gbox) { const POINT4D *p = getPoint4d_cp(pa, 0); gbox->xmax = gbox->xmin = p->x; gbox->ymax = gbox->ymin = p->y; gbox->zmax = gbox->zmin = p->z; gbox->mmax = gbox->mmin = p->m; for (uint32_t i = 1; i < pa->npoints; i++) { p = getPoint4d_cp(pa, i); gbox->xmin = FP_MIN(gbox->xmin, p->x); gbox->xmax = FP_MAX(gbox->xmax, p->x); gbox->ymin = FP_MIN(gbox->ymin, p->y); gbox->ymax = FP_MAX(gbox->ymax, p->y); gbox->zmin = FP_MIN(gbox->zmin, p->z); gbox->zmax = FP_MAX(gbox->zmax, p->z); gbox->mmin = FP_MIN(gbox->mmin, p->m); gbox->mmax = FP_MAX(gbox->mmax, p->m); } } int ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox) { if (!pa || pa->npoints == 0) return LW_FAILURE; if (!gbox) return LW_FAILURE; int has_z = FLAGS_GET_Z(pa->flags); int has_m = FLAGS_GET_M(pa->flags); gbox->flags = lwflags(has_z, has_m, 0); LWDEBUGF(4, "ptarray_calculate_gbox Z: %d M: %d", has_z, has_m); int coordinates = 2 + has_z + has_m; switch (coordinates) { case 2: { ptarray_calculate_gbox_cartesian_2d(pa, gbox); break; } case 3: { if (has_z) { ptarray_calculate_gbox_cartesian_3d(pa, gbox); } else { double zmin = gbox->zmin; double zmax = gbox->zmax; ptarray_calculate_gbox_cartesian_3d(pa, gbox); gbox->mmin = gbox->zmin; gbox->mmax = gbox->zmax; gbox->zmin = zmin; gbox->zmax = zmax; } break; } default: { ptarray_calculate_gbox_cartesian_4d(pa, gbox); break; } } return LW_SUCCESS; } static int lwcircstring_calculate_gbox_cartesian(LWCIRCSTRING *curve, GBOX *gbox) { GBOX tmp; POINT4D p1, p2, p3; uint32_t i; if (!curve) return LW_FAILURE; if (curve->points->npoints < 3) return LW_FAILURE; tmp.flags = lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0); /* Initialize */ gbox->xmin = gbox->ymin = gbox->zmin = gbox->mmin = FLT_MAX; gbox->xmax = gbox->ymax = gbox->zmax = gbox->mmax = -1*FLT_MAX; for ( i = 2; i < curve->points->npoints; i += 2 ) { getPoint4d_p(curve->points, i-2, &p1); getPoint4d_p(curve->points, i-1, &p2); getPoint4d_p(curve->points, i, &p3); if (lw_arc_calculate_gbox_cartesian(&p1, &p2, &p3, &tmp) == LW_FAILURE) continue; gbox_merge(&tmp, gbox); } return LW_SUCCESS; } static int lwpoint_calculate_gbox_cartesian(LWPOINT *point, GBOX *gbox) { if ( ! point ) return LW_FAILURE; return ptarray_calculate_gbox_cartesian( point->point, gbox ); } static int lwline_calculate_gbox_cartesian(LWLINE *line, GBOX *gbox) { if ( ! line ) return LW_FAILURE; return ptarray_calculate_gbox_cartesian( line->points, gbox ); } static int lwtriangle_calculate_gbox_cartesian(LWTRIANGLE *triangle, GBOX *gbox) { if ( ! triangle ) return LW_FAILURE; return ptarray_calculate_gbox_cartesian( triangle->points, gbox ); } static int lwpoly_calculate_gbox_cartesian(LWPOLY *poly, GBOX *gbox) { if ( ! poly ) return LW_FAILURE; if ( poly->nrings == 0 ) return LW_FAILURE; /* Just need to check outer ring */ return ptarray_calculate_gbox_cartesian( poly->rings[0], gbox ); } static int lwcollection_calculate_gbox_cartesian(LWCOLLECTION *coll, GBOX *gbox) { GBOX subbox; uint32_t i; int result = LW_FAILURE; int first = LW_TRUE; assert(coll); if ( (coll->ngeoms == 0) || !gbox) return LW_FAILURE; subbox.flags = coll->flags; for ( i = 0; i < coll->ngeoms; i++ ) { if ( lwgeom_calculate_gbox_cartesian((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS ) { /* Keep a copy of the sub-bounding box for later if ( coll->geoms[i]->bbox ) lwfree(coll->geoms[i]->bbox); coll->geoms[i]->bbox = gbox_copy(&subbox); */ if ( first ) { gbox_duplicate(&subbox, gbox); first = LW_FALSE; } else { gbox_merge(&subbox, gbox); } result = LW_SUCCESS; } } return result; } int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox) { if ( ! lwgeom ) return LW_FAILURE; LWDEBUGF(4, "lwgeom_calculate_gbox got type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type)); switch (lwgeom->type) { case POINTTYPE: return lwpoint_calculate_gbox_cartesian((LWPOINT *)lwgeom, gbox); case LINETYPE: return lwline_calculate_gbox_cartesian((LWLINE *)lwgeom, gbox); case CIRCSTRINGTYPE: return lwcircstring_calculate_gbox_cartesian((LWCIRCSTRING *)lwgeom, gbox); case POLYGONTYPE: return lwpoly_calculate_gbox_cartesian((LWPOLY *)lwgeom, gbox); case TRIANGLETYPE: return lwtriangle_calculate_gbox_cartesian((LWTRIANGLE *)lwgeom, gbox); case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_calculate_gbox_cartesian((LWCOLLECTION *)lwgeom, gbox); } /* Never get here, please. */ lwerror("unsupported type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type)); return LW_FAILURE; } void gbox_float_round(GBOX *gbox) { gbox->xmin = next_float_down(gbox->xmin); gbox->xmax = next_float_up(gbox->xmax); gbox->ymin = next_float_down(gbox->ymin); gbox->ymax = next_float_up(gbox->ymax); if ( FLAGS_GET_M(gbox->flags) ) { gbox->mmin = next_float_down(gbox->mmin); gbox->mmax = next_float_up(gbox->mmax); } if ( FLAGS_GET_Z(gbox->flags) ) { gbox->zmin = next_float_down(gbox->zmin); gbox->zmax = next_float_up(gbox->zmax); } } inline static uint64_t uint64_interleave_2(uint64_t x, uint64_t y) { x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL; x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL; x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL; x = (x | (x << 2)) & 0x3333333333333333ULL; x = (x | (x << 1)) & 0x5555555555555555ULL; y = (y | (y << 16)) & 0x0000FFFF0000FFFFULL; y = (y | (y << 8)) & 0x00FF00FF00FF00FFULL; y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0FULL; y = (y | (y << 2)) & 0x3333333333333333ULL; y = (y | (y << 1)) & 0x5555555555555555ULL; return x | (y << 1); } /* Based on https://github.com/rawrunprotected/hilbert_curves Public Domain code */ inline static uint64_t uint32_hilbert(uint32_t px, uint32_t py) { uint64_t x = px; uint64_t y = py; uint64_t A, B, C, D; // Initial prefix scan round, prime with x and y { uint64_t a = x ^ y; uint64_t b = 0xFFFFFFFFULL ^ a; uint64_t c = 0xFFFFFFFFULL ^ (x | y); uint64_t d = x & (y ^ 0xFFFFFFFFULL); A = a | (b >> 1); B = (a >> 1) ^ a; C = ((c >> 1) ^ (b & (d >> 1))) ^ c; D = ((a & (c >> 1)) ^ (d >> 1)) ^ d; } { uint64_t a = A; uint64_t b = B; uint64_t c = C; uint64_t d = D; A = ((a & (a >> 2)) ^ (b & (b >> 2))); B = ((a & (b >> 2)) ^ (b & ((a ^ b) >> 2))); C ^= ((a & (c >> 2)) ^ (b & (d >> 2))); D ^= ((b & (c >> 2)) ^ ((a ^ b) & (d >> 2))); } { uint64_t a = A; uint64_t b = B; uint64_t c = C; uint64_t d = D; A = ((a & (a >> 4)) ^ (b & (b >> 4))); B = ((a & (b >> 4)) ^ (b & ((a ^ b) >> 4))); C ^= ((a & (c >> 4)) ^ (b & (d >> 4))); D ^= ((b & (c >> 4)) ^ ((a ^ b) & (d >> 4))); } { uint64_t a = A; uint64_t b = B; uint64_t c = C; uint64_t d = D; A = ((a & (a >> 8)) ^ (b & (b >> 8))); B = ((a & (b >> 8)) ^ (b & ((a ^ b) >> 8))); C ^= ((a & (c >> 8)) ^ (b & (d >> 8))); D ^= ((b & (c >> 8)) ^ ((a ^ b) & (d >> 8))); } { uint64_t a = A; uint64_t b = B; uint64_t c = C; uint64_t d = D; C ^= ((a & (c >> 16)) ^ (b & (d >> 16))); D ^= ((b & (c >> 16)) ^ ((a ^ b) & (d >> 16))); } // Undo transformation prefix scan uint64_t a = C ^ (C >> 1); uint64_t b = D ^ (D >> 1); // Recover index bits uint64_t i0 = x ^ y; uint64_t i1 = b | (0xFFFFFFFFULL ^ (i0 | a)); return uint64_interleave_2(i0, i1); } uint64_t gbox_get_sortable_hash(const GBOX *g, const int32_t srid) { union floatuint { uint32_t u; float f; }; union floatuint x, y; /* * Since in theory the bitwise representation of an IEEE * float is sortable (exponents come before mantissa, etc) * we just copy the bits directly into an int and then * interleave those ints. */ if (FLAGS_GET_GEODETIC(g->flags)) { GEOGRAPHIC_POINT gpt; POINT3D p; p.x = (g->xmax + g->xmin) / 2.0; p.y = (g->ymax + g->ymin) / 2.0; p.z = (g->zmax + g->zmin) / 2.0; normalize(&p); cart2geog(&p, &gpt); /* We know range for geography, so build the curve taking it into account */ x.f = 1.5 + gpt.lon / 512.0; y.f = 1.5 + gpt.lat / 256.0; } else { x.f = (g->xmax + g->xmin) / 2; y.f = (g->ymax + g->ymin) / 2; /* * Tweak for popular SRID values: push floating point values into 1..2 range, * a region where exponent is constant and thus Hilbert curve * doesn't have compression artifact when X or Y value is close to 0. * If someone has out of bounds value it will still expose the arifact but not crash. * TODO: reconsider when we will have machinery to properly get bounds by SRID. */ if (srid == 3857 || srid == 3395) { x.f = 1.5 + x.f / 67108864.0; y.f = 1.5 + y.f / 67108864.0; } else if (srid == 4326) { x.f = 1.5 + x.f / 512.0; y.f = 1.5 + y.f / 256.0; } } return uint32_hilbert(y.u, x.u); } lwgeom/src/liblwgeom/lwgeom_transform.c0000644000176200001440000003034213773172540020060 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2003 Refractions Research Inc. * **********************************************************************/ #include "../postgis_config.h" #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include /** convert decimal degress to radians */ static void to_rad(POINT4D *pt) { pt->x *= M_PI/180.0; pt->y *= M_PI/180.0; } /** convert radians to decimal degress */ static void to_dec(POINT4D *pt) { pt->x *= 180.0/M_PI; pt->y *= 180.0/M_PI; } /***************************************************************************/ #if POSTGIS_PROJ_VERSION < 60 static int point4d_transform(POINT4D *pt, LWPROJ *pj) { POINT3D orig_pt = {pt->x, pt->y, pt->z}; /* Copy for error report*/ if (pj_is_latlong(pj->pj_from)) to_rad(pt) ; LWDEBUGF(4, "transforming POINT(%f %f) from '%s' to '%s'", orig_pt.x, orig_pt.y, pj_get_def(pj->pj_from,0), pj_get_def(pj->pj_to,0)); if (pj_transform(pj->pj_from, pj->pj_to, 1, 0, &(pt->x), &(pt->y), &(pt->z)) != 0) { int pj_errno_val = *pj_get_errno_ref(); if (pj_errno_val == -38) { lwnotice("PostGIS was unable to transform the point because either no grid" " shift files were found, or the point does not lie within the " "range for which the grid shift is defined. Refer to the " "ST_Transform() section of the PostGIS manual for details on how " "to configure PostGIS to alter this behaviour."); lwerror("transform: couldn't project point (%g %g %g): %s (%d)", orig_pt.x, orig_pt.y, orig_pt.z, pj_strerrno(pj_errno_val), pj_errno_val); } else { lwerror("transform: %s (%d)", pj_strerrno(pj_errno_val), pj_errno_val); } return LW_FAILURE; } if (pj_is_latlong(pj->pj_to)) to_dec(pt); return LW_SUCCESS; } /** * Transform given POINTARRAY * from inpj projection to outpj projection */ int ptarray_transform(POINTARRAY *pa, LWPROJ *pj) { uint32_t i; POINT4D p; for ( i = 0; i < pa->npoints; i++ ) { getPoint4d_p(pa, i, &p); if ( ! point4d_transform(&p, pj) ) return LW_FAILURE; ptarray_set_point4d(pa, i, &p); } return LW_SUCCESS; } int lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr) { char *pj_errstr; int rv; LWPROJ pj; pj.pj_from = projpj_from_string(instr); if (!pj.pj_from) { pj_errstr = pj_strerrno(*pj_get_errno_ref()); if (!pj_errstr) pj_errstr = ""; lwerror("could not parse proj string '%s'", instr); return LW_FAILURE; } pj.pj_to = projpj_from_string(outstr); if (!pj.pj_to) { pj_free(pj.pj_from); pj_errstr = pj_strerrno(*pj_get_errno_ref()); if (!pj_errstr) pj_errstr = ""; lwerror("could not parse proj string '%s'", outstr); return LW_FAILURE; } rv = lwgeom_transform(geom, &pj); pj_free(pj.pj_from); pj_free(pj.pj_to); return rv; } /** * Transform given LWGEOM geometry * from inpj projection to outpj projection */ int lwgeom_transform(LWGEOM *geom, LWPROJ *pj) { uint32_t i; /* No points to transform in an empty! */ if ( lwgeom_is_empty(geom) ) return LW_SUCCESS; switch(geom->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case TRIANGLETYPE: { LWLINE *g = (LWLINE*)geom; if ( ! ptarray_transform(g->points, pj) ) return LW_FAILURE; break; } case POLYGONTYPE: { LWPOLY *g = (LWPOLY*)geom; for ( i = 0; i < g->nrings; i++ ) { if ( ! ptarray_transform(g->rings[i], pj) ) return LW_FAILURE; } break; } case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: { LWCOLLECTION *g = (LWCOLLECTION*)geom; for ( i = 0; i < g->ngeoms; i++ ) { if ( ! lwgeom_transform(g->geoms[i], pj) ) return LW_FAILURE; } break; } default: { lwerror("lwgeom_transform: Cannot handle type '%s'", lwtype_name(geom->type)); return LW_FAILURE; } } return LW_SUCCESS; } projPJ projpj_from_string(const char *str1) { if (!str1 || str1[0] == '\0') { return NULL; } return pj_init_plus(str1); } /***************************************************************************/ #else /* POSTGIS_PROJ_VERION >= 60 */ static uint8_t proj_crs_is_swapped(const PJ *pj_crs) { PJ *pj_cs; uint8_t rv = LW_FALSE; if (proj_get_type(pj_crs) == PJ_TYPE_COMPOUND_CRS) { PJ *pj_horiz_crs = proj_crs_get_sub_crs(NULL, pj_crs, 0); if (!pj_horiz_crs) lwerror("%s: proj_crs_get_sub_crs returned NULL", __func__); pj_cs = proj_crs_get_coordinate_system(NULL, pj_horiz_crs); proj_destroy(pj_horiz_crs); } else if (proj_get_type(pj_crs) == PJ_TYPE_BOUND_CRS) { PJ *pj_src_crs = proj_get_source_crs(NULL, pj_crs); if (!pj_src_crs) lwerror("%s: proj_get_source_crs returned NULL", __func__); pj_cs = proj_crs_get_coordinate_system(NULL, pj_src_crs); proj_destroy(pj_src_crs); } else { pj_cs = proj_crs_get_coordinate_system(NULL, pj_crs); } if (!pj_cs) lwerror("%s: proj_crs_get_coordinate_system returned NULL", __func__); int axis_count = proj_cs_get_axis_count(NULL, pj_cs); if (axis_count > 0) { const char *out_name, *out_abbrev, *out_direction; double out_unit_conv_factor; const char *out_unit_name, *out_unit_auth_name, *out_unit_code; /* Read only first axis, see if it is degrees / north */ proj_cs_get_axis_info(NULL, pj_cs, 0, &out_name, &out_abbrev, &out_direction, &out_unit_conv_factor, &out_unit_name, &out_unit_auth_name, &out_unit_code); rv = (strcasecmp(out_direction, "north") == 0); } proj_destroy(pj_cs); return rv; } LWPROJ * lwproj_from_PJ(PJ *pj, int8_t extra_geography_data) { PJ *pj_source_crs = proj_get_source_crs(NULL, pj); uint8_t source_is_latlong = LW_FALSE; double out_semi_major_metre = DBL_MAX, out_semi_minor_metre = DBL_MAX; if (!pj_source_crs) { lwerror("%s: unable to access source crs", __func__); return NULL; } uint8_t source_swapped = proj_crs_is_swapped(pj_source_crs); /* We only care about the extra values if there is no transformation */ if (!extra_geography_data) { proj_destroy(pj_source_crs); } else { PJ *pj_ellps; double out_inv_flattening; int out_is_semi_minor_computed; PJ_TYPE pj_type = proj_get_type(pj_source_crs); if (pj_type == PJ_TYPE_UNKNOWN) { proj_destroy(pj_source_crs); lwerror("%s: unable to access source crs type", __func__); return NULL; } source_is_latlong = (pj_type == PJ_TYPE_GEOGRAPHIC_2D_CRS) || (pj_type == PJ_TYPE_GEOGRAPHIC_3D_CRS); pj_ellps = proj_get_ellipsoid(NULL, pj_source_crs); proj_destroy(pj_source_crs); if (!pj_ellps) { lwerror("%s: unable to access source crs ellipsoid", __func__); return NULL; } if (!proj_ellipsoid_get_parameters(NULL, pj_ellps, &out_semi_major_metre, &out_semi_minor_metre, &out_is_semi_minor_computed, &out_inv_flattening)) { proj_destroy(pj_ellps); lwerror("%s: unable to access source crs ellipsoid parameters", __func__); return NULL; } proj_destroy(pj_ellps); } PJ *pj_target_crs = proj_get_target_crs(NULL, pj); if (!pj_target_crs) { lwerror("%s: unable to access target crs", __func__); return NULL; } uint8_t target_swapped = proj_crs_is_swapped(pj_target_crs); proj_destroy(pj_target_crs); LWPROJ *lp = lwalloc(sizeof(LWPROJ)); lp->pj = pj; lp->source_swapped = source_swapped; lp->target_swapped = target_swapped; lp->source_is_latlong = source_is_latlong; lp->source_semi_major_metre = out_semi_major_metre; lp->source_semi_minor_metre = out_semi_minor_metre; return lp; } int lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr) { PJ *pj = proj_create_crs_to_crs(NULL, instr, outstr, NULL); if (!pj) { PJ *pj_in = proj_create(NULL, instr); if (!pj_in) { lwerror("could not parse proj string '%s'", instr); } proj_destroy(pj_in); PJ *pj_out = proj_create(NULL, outstr); if (!pj_out) { lwerror("could not parse proj string '%s'", outstr); } proj_destroy(pj_out); lwerror("%s: Failed to transform", __func__); return LW_FAILURE; } LWPROJ *lp = lwproj_from_PJ(pj, LW_FALSE); int ret = lwgeom_transform(geom, lp); proj_destroy(pj); lwfree(lp); return ret; } int lwgeom_transform(LWGEOM *geom, LWPROJ *pj) { uint32_t i; /* No points to transform in an empty! */ if (lwgeom_is_empty(geom)) return LW_SUCCESS; switch(geom->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case TRIANGLETYPE: { LWLINE *g = (LWLINE*)geom; if (!ptarray_transform(g->points, pj)) return LW_FAILURE; break; } case POLYGONTYPE: { LWPOLY *g = (LWPOLY*)geom; for (i = 0; i < g->nrings; i++) { if (!ptarray_transform(g->rings[i], pj)) return LW_FAILURE; } break; } case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: { LWCOLLECTION *g = (LWCOLLECTION*)geom; for (i = 0; i < g->ngeoms; i++) { if (!lwgeom_transform(g->geoms[i], pj)) return LW_FAILURE; } break; } default: { lwerror("lwgeom_transform: Cannot handle type '%s'", lwtype_name(geom->type)); return LW_FAILURE; } } return LW_SUCCESS; } int ptarray_transform(POINTARRAY *pa, LWPROJ *pj) { uint32_t i; POINT4D p; size_t n_converted; size_t n_points = pa->npoints; size_t point_size = ptarray_point_size(pa); int has_z = ptarray_has_z(pa); double *pa_double = (double*)(pa->serialized_pointlist); /* Convert to radians if necessary */ if (proj_angular_input(pj->pj, PJ_FWD)) { for (i = 0; i < pa->npoints; i++) { getPoint4d_p(pa, i, &p); to_rad(&p); } } if (pj->source_swapped) ptarray_swap_ordinates(pa, LWORD_X, LWORD_Y); if (n_points == 1) { /* For single points it's faster to call proj_trans */ PJ_XYZT v = {pa_double[0], pa_double[1], has_z ? pa_double[2] : 0.0, 0.0}; PJ_COORD v2; v2.xyzt = v; PJ_COORD t = proj_trans(pj->pj, PJ_FWD, v2); int pj_errno_val = proj_errno(pj->pj); if (pj_errno_val) { lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val); return LW_FAILURE; } pa_double[0] = (t.xyzt).x; pa_double[1] = (t.xyzt).y; if (has_z) pa_double[2] = (t.xyzt).z; } else { /* * size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction, * double *x, size_t sx, size_t nx, * double *y, size_t sy, size_t ny, * double *z, size_t sz, size_t nz, * double *t, size_t st, size_t nt) */ n_converted = proj_trans_generic(pj->pj, PJ_FWD, pa_double, point_size, n_points, /* X */ pa_double + 1, point_size, n_points, /* Y */ has_z ? pa_double + 2 : NULL, has_z ? point_size : 0, has_z ? n_points : 0, /* Z */ NULL, 0, 0 /* M */ ); if (n_converted != n_points) { lwerror("ptarray_transform: converted (%d) != input (%d)", n_converted, n_points); return LW_FAILURE; } int pj_errno_val = proj_errno(pj->pj); if (pj_errno_val) { lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val); return LW_FAILURE; } } if (pj->target_swapped) ptarray_swap_ordinates(pa, LWORD_X, LWORD_Y); /* Convert radians to degrees if necessary */ if (proj_angular_output(pj->pj, PJ_FWD)) { for (i = 0; i < pa->npoints; i++) { getPoint4d_p(pa, i, &p); to_dec(&p); } } return LW_SUCCESS; } #endif lwgeom/src/liblwgeom/lwhomogenize.c0000644000176200001440000001445713773172540017213 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2010 Olivier Courtin * **********************************************************************/ #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" typedef struct { int cnt[NUMTYPES]; LWCOLLECTION* buf[NUMTYPES]; } HomogenizeBuffer; static void init_homogenizebuffer(HomogenizeBuffer *buffer) { int i; for ( i = 0; i < NUMTYPES; i++ ) { buffer->cnt[i] = 0; buffer->buf[i] = NULL; } } /* static void free_homogenizebuffer(HomogenizeBuffer *buffer) { int i; for ( i = 0; i < NUMTYPES; i++ ) { if ( buffer->buf[i] ) { lwcollection_free(buffer->buf[i]); } } } */ /* ** Given a generic collection, return the "simplest" form. ** ** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) ** ** In general, if the subcomponents are homogeneous, return a properly ** typed collection. ** Otherwise, return a generic collection, with the subtypes in minimal ** typed collections. */ static void lwcollection_build_buffer(const LWCOLLECTION *col, HomogenizeBuffer *buffer) { uint32_t i; if (!col || lwcollection_is_empty(col)) return; for (i = 0; i < col->ngeoms; i++) { LWGEOM *geom = col->geoms[i]; switch (geom->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case POLYGONTYPE: /* Init if necessary */ if (!buffer->buf[geom->type]) { LWCOLLECTION *bufcol = lwcollection_construct_empty( COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); bufcol->type = lwtype_get_collectiontype(geom->type); buffer->buf[geom->type] = bufcol; } /* Add sub-geom to buffer */ lwcollection_add_lwgeom(buffer->buf[geom->type], lwgeom_clone(geom)); /* Increment count for this singleton type */ buffer->cnt[geom->type]++; break; default: lwcollection_build_buffer(lwgeom_as_lwcollection(geom), buffer); break; } } } static LWGEOM* lwcollection_homogenize(const LWCOLLECTION *col) { int i; int ntypes = 0; int type = 0; LWGEOM *outgeom = NULL; HomogenizeBuffer buffer; /* Sort all the parts into a buffer */ init_homogenizebuffer(&buffer); lwcollection_build_buffer(col, &buffer); /* Check for homogeneity */ for ( i = 0; i < NUMTYPES; i++ ) { if ( buffer.cnt[i] > 0 ) { ntypes++; type = i; } } /* No types? Huh. Return empty. */ if ( ntypes == 0 ) { LWCOLLECTION *outcol; outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); outgeom = lwcollection_as_lwgeom(outcol); } /* One type, return homogeneous collection */ else if ( ntypes == 1 ) { LWCOLLECTION *outcol; outcol = buffer.buf[type]; if ( outcol->ngeoms == 1 ) { outgeom = outcol->geoms[0]; outcol->ngeoms=0; lwcollection_free(outcol); } else { outgeom = lwcollection_as_lwgeom(outcol); } outgeom->srid = col->srid; } /* Bah, more than out type, return anonymous collection */ else if ( ntypes > 1 ) { int j; LWCOLLECTION *outcol; outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); for ( j = 0; j < NUMTYPES; j++ ) { if ( buffer.buf[j] ) { LWCOLLECTION *bcol = buffer.buf[j]; if ( bcol->ngeoms == 1 ) { lwcollection_add_lwgeom(outcol, bcol->geoms[0]); bcol->ngeoms=0; lwcollection_free(bcol); } else { lwcollection_add_lwgeom(outcol, lwcollection_as_lwgeom(bcol)); } } } outgeom = lwcollection_as_lwgeom(outcol); } return outgeom; } /* ** Given a generic geometry, return the "simplest" form. ** ** eg: ** LINESTRING() => LINESTRING() ** ** MULTILINESTRING(with a single line) => LINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) */ LWGEOM * lwgeom_homogenize(const LWGEOM *geom) { LWGEOM *hgeom; /* EMPTY Geometry */ if (lwgeom_is_empty(geom)) { if( lwgeom_is_collection(geom) ) { return lwcollection_as_lwgeom(lwcollection_construct_empty(geom->type, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom))); } return lwgeom_clone(geom); } switch (geom->type) { /* Return simple geometries untouched */ case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case POLYGONTYPE: return lwgeom_clone(geom); /* Process homogeneous geometries lightly */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: { LWCOLLECTION *col = (LWCOLLECTION*)geom; /* Strip single-entry multi-geometries down to singletons */ if ( col->ngeoms == 1 ) { hgeom = lwgeom_clone((LWGEOM*)(col->geoms[0])); hgeom->srid = geom->srid; if (geom->bbox) hgeom->bbox = gbox_copy(geom->bbox); return hgeom; } /* Return proper multigeometry untouched */ return lwgeom_clone(geom); } /* Work on anonymous collections separately */ case COLLECTIONTYPE: return lwcollection_homogenize((LWCOLLECTION *) geom); } /* Unknown type */ lwerror("lwgeom_homogenize: Geometry Type not supported (%i)", lwtype_name(geom->type)); return NULL; /* Never get here! */ } lwgeom/src/liblwgeom/lwcollection.c0000644000176200001440000003172613773172540017200 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" #define CHECK_LWGEOM_ZM 1 void lwcollection_release(LWCOLLECTION *lwcollection) { lwgeom_release(lwcollection_as_lwgeom(lwcollection)); } LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms) { LWCOLLECTION *ret; int hasz, hasm; #ifdef CHECK_LWGEOM_ZM char zm; uint32_t i; #endif LWDEBUGF(2, "lwcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms); if( ! lwtype_is_collection(type) ) lwerror("Non-collection type specified in collection constructor!"); hasz = 0; hasm = 0; if ( ngeoms > 0 ) { hasz = FLAGS_GET_Z(geoms[0]->flags); hasm = FLAGS_GET_M(geoms[0]->flags); #ifdef CHECK_LWGEOM_ZM zm = FLAGS_GET_ZM(geoms[0]->flags); LWDEBUGF(3, "lwcollection_construct type[0]=%d", geoms[0]->type); for (i=1; itype); if ( zm != FLAGS_GET_ZM(geoms[i]->flags) ) lwerror("lwcollection_construct: mixed dimension geometries: %d/%d", zm, FLAGS_GET_ZM(geoms[i]->flags)); } #endif } ret = lwalloc(sizeof(LWCOLLECTION)); ret->type = type; ret->flags = lwflags(hasz,hasm,0); FLAGS_SET_BBOX(ret->flags, bbox?1:0); ret->srid = srid; ret->ngeoms = ngeoms; ret->maxgeoms = ngeoms; ret->geoms = geoms; ret->bbox = bbox; return ret; } LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm) { LWCOLLECTION *ret; if( ! lwtype_is_collection(type) ) { lwerror("Non-collection type specified in collection constructor!"); return NULL; } ret = lwalloc(sizeof(LWCOLLECTION)); ret->type = type; ret->flags = lwflags(hasz,hasm,0); ret->srid = srid; ret->ngeoms = 0; ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */ ret->geoms = lwalloc(ret->maxgeoms * sizeof(LWGEOM*)); ret->bbox = NULL; return ret; } LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, int gnum) { return (LWGEOM *)col->geoms[gnum]; } /** * @brief Clone #LWCOLLECTION object. #POINTARRAY are not copied. * Bbox is cloned if present in input. */ LWCOLLECTION * lwcollection_clone(const LWCOLLECTION *g) { uint32_t i; LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION)); memcpy(ret, g, sizeof(LWCOLLECTION)); if ( g->ngeoms > 0 ) { ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms); for (i=0; ingeoms; i++) { ret->geoms[i] = lwgeom_clone(g->geoms[i]); } if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); } else { ret->bbox = NULL; /* empty collection */ ret->geoms = NULL; } return ret; } /** * @brief Deep clone #LWCOLLECTION object. #POINTARRAY are copied. */ LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *g) { uint32_t i; LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION)); memcpy(ret, g, sizeof(LWCOLLECTION)); if ( g->ngeoms > 0 ) { ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms); for (i=0; ingeoms; i++) { ret->geoms[i] = lwgeom_clone_deep(g->geoms[i]); } if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); } else { ret->bbox = NULL; /* empty collection */ ret->geoms = NULL; } return ret; } /** * Ensure the collection can hold up at least ngeoms */ void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms) { if ( ngeoms <= col->maxgeoms ) return; /* Allocate more space if we need it */ do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms ); col->geoms = lwrealloc(col->geoms, sizeof(LWGEOM*) * col->maxgeoms); } /** * Appends geom to the collection managed by col. Does not copy or * clone, simply takes a reference on the passed geom. */ LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom) { if (!col || !geom) return NULL; if (!col->geoms && (col->ngeoms || col->maxgeoms)) { lwerror("Collection is in inconsistent state. Null memory but non-zero collection counts."); return NULL; } /* Check type compatibility */ if ( ! lwcollection_allows_subtype(col->type, geom->type) ) { lwerror("%s cannot contain %s element", lwtype_name(col->type), lwtype_name(geom->type)); return NULL; } /* In case this is a truly empty, make some initial space */ if (!col->geoms) { col->maxgeoms = 2; col->ngeoms = 0; col->geoms = lwalloc(col->maxgeoms * sizeof(LWGEOM*)); } /* Allocate more space if we need it */ lwcollection_reserve(col, col->ngeoms + 1); #if PARANOIA_LEVEL > 1 /* See http://trac.osgeo.org/postgis/ticket/2933 */ /* Make sure we don't already have a reference to this geom */ { uint32_t i = 0; for (i = 0; i < col->ngeoms; i++) { if (col->geoms[i] == geom) { lwerror("%s [%d] found duplicate geometry in collection %p == %p", __FILE__, __LINE__, col->geoms[i], geom); return col; } } } #endif col->geoms[col->ngeoms] = (LWGEOM*)geom; col->ngeoms++; return col; } /** * Appends all geometries from col2 to col1 in place. * Caller is responsible to release col2. */ LWCOLLECTION * lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2) { uint32_t i; if (!col1 || !col2) return NULL; for (i = 0; i < col2->ngeoms; i++) col1 = lwcollection_add_lwgeom(col1, col2->geoms[i]); return col1; } LWCOLLECTION* lwcollection_segmentize2d(const LWCOLLECTION* col, double dist) { uint32_t i, j; LWGEOM** newgeoms; if (!col->ngeoms) return lwcollection_clone(col); newgeoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms); for (i = 0; i < col->ngeoms; i++) { newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist); if (!newgeoms[i]) { for (j = 0; j < i; j++) lwgeom_free(newgeoms[j]); lwfree(newgeoms); return NULL; } } return lwcollection_construct( col->type, col->srid, NULL, col->ngeoms, newgeoms); } /** @brief check for same geometry composition * */ char lwcollection_same(const LWCOLLECTION *c1, const LWCOLLECTION *c2) { uint32_t i; LWDEBUG(2, "lwcollection_same called"); if ( c1->type != c2->type ) return LW_FALSE; if ( c1->ngeoms != c2->ngeoms ) return LW_FALSE; for ( i = 0; i < c1->ngeoms; i++ ) { if ( ! lwgeom_same(c1->geoms[i], c2->geoms[i]) ) return LW_FALSE; } /* Former method allowed out-of-order equality between collections hit = lwalloc(sizeof(uint32_t)*c1->ngeoms); memset(hit, 0, sizeof(uint32_t)*c1->ngeoms); for (i=0; ingeoms; i++) { char found=0; for (j=0; jngeoms; j++) { if ( hit[j] ) continue; if ( lwgeom_same(c1->geoms[i], c2->geoms[j]) ) { hit[j] = 1; found=1; break; } } if ( ! found ) return LW_FALSE; } */ return LW_TRUE; } int lwcollection_ngeoms(const LWCOLLECTION *col) { uint32_t i; int ngeoms = 0; if ( ! col ) { lwerror("Null input geometry."); return 0; } for ( i = 0; i < col->ngeoms; i++ ) { if ( col->geoms[i]) { switch (col->geoms[i]->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case POLYGONTYPE: ngeoms += 1; break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: ngeoms += col->ngeoms; break; case COLLECTIONTYPE: ngeoms += lwcollection_ngeoms((LWCOLLECTION*)col->geoms[i]); break; } } } return ngeoms; } void lwcollection_free(LWCOLLECTION *col) { uint32_t i; if ( ! col ) return; if ( col->bbox ) { lwfree(col->bbox); } for ( i = 0; i < col->ngeoms; i++ ) { LWDEBUGF(4,"freeing geom[%d]", i); if ( col->geoms && col->geoms[i] ) lwgeom_free(col->geoms[i]); } if ( col->geoms ) { lwfree(col->geoms); } lwfree(col); } /** * Takes a potentially heterogeneous collection and returns a homogeneous * collection consisting only of the specified type. * WARNING: the output will contain references to geometries in the input, * so the result must be carefully released, not freed. */ LWCOLLECTION* lwcollection_extract(LWCOLLECTION* col, int type) { uint32_t i = 0; LWGEOM** geomlist; LWCOLLECTION* outcol; int geomlistsize = 16; int geomlistlen = 0; uint8_t outtype; if (!col) return NULL; switch (type) { case POINTTYPE: outtype = MULTIPOINTTYPE; break; case LINETYPE: outtype = MULTILINETYPE; break; case POLYGONTYPE: outtype = MULTIPOLYGONTYPE; break; default: lwerror( "Only POLYGON, LINESTRING and POINT are supported by " "lwcollection_extract. %s requested.", lwtype_name(type)); return NULL; } geomlist = lwalloc(sizeof(LWGEOM*) * geomlistsize); /* Process each sub-geometry */ for (i = 0; i < col->ngeoms; i++) { int subtype = col->geoms[i]->type; /* Don't bother adding empty sub-geometries */ if (lwgeom_is_empty(col->geoms[i])) continue; /* Copy our sub-types into the output list */ if (subtype == type) { /* We've over-run our buffer, double the memory segment */ if (geomlistlen == geomlistsize) { geomlistsize *= 2; geomlist = lwrealloc( geomlist, sizeof(LWGEOM*) * geomlistsize); } geomlist[geomlistlen] = lwgeom_clone(col->geoms[i]); geomlistlen++; } /* Recurse into sub-collections */ if (lwtype_is_collection(subtype)) { uint32_t j = 0; LWCOLLECTION* tmpcol = lwcollection_extract( (LWCOLLECTION*)col->geoms[i], type); for (j = 0; j < tmpcol->ngeoms; j++) { /* We've over-run our buffer, double the memory * segment */ if (geomlistlen == geomlistsize) { geomlistsize *= 2; geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize); } geomlist[geomlistlen] = tmpcol->geoms[j]; geomlistlen++; } if (tmpcol->ngeoms) lwfree(tmpcol->geoms); if (tmpcol->bbox) lwfree(tmpcol->bbox); lwfree(tmpcol); } } if (geomlistlen > 0) { GBOX gbox; outcol = lwcollection_construct( outtype, col->srid, NULL, geomlistlen, geomlist); lwgeom_calculate_gbox((LWGEOM*)outcol, &gbox); outcol->bbox = gbox_copy(&gbox); } else { lwfree(geomlist); outcol = lwcollection_construct_empty(outtype, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); } return outcol; } LWCOLLECTION* lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm) { LWCOLLECTION *colout; /* Return 2D empty */ if( lwcollection_is_empty(col) ) { colout = lwcollection_construct_empty(col->type, col->srid, hasz, hasm); } else { uint32_t i; LWGEOM **geoms = NULL; geoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms); for( i = 0; i < col->ngeoms; i++ ) { geoms[i] = lwgeom_force_dims(col->geoms[i], hasz, hasm); } colout = lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, geoms); } return colout; } uint32_t lwcollection_count_vertices(LWCOLLECTION *col) { uint32_t i = 0; uint32_t v = 0; /* vertices */ assert(col); for ( i = 0; i < col->ngeoms; i++ ) { v += lwgeom_count_vertices(col->geoms[i]); } return v; } int lwcollection_allows_subtype(int collectiontype, int subtype) { if ( collectiontype == COLLECTIONTYPE ) return LW_TRUE; if ( collectiontype == MULTIPOINTTYPE && subtype == POINTTYPE ) return LW_TRUE; if ( collectiontype == MULTILINETYPE && subtype == LINETYPE ) return LW_TRUE; if ( collectiontype == MULTIPOLYGONTYPE && subtype == POLYGONTYPE ) return LW_TRUE; if ( collectiontype == COMPOUNDTYPE && (subtype == LINETYPE || subtype == CIRCSTRINGTYPE) ) return LW_TRUE; if ( collectiontype == CURVEPOLYTYPE && (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) ) return LW_TRUE; if ( collectiontype == MULTICURVETYPE && (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) ) return LW_TRUE; if ( collectiontype == MULTISURFACETYPE && (subtype == POLYGONTYPE || subtype == CURVEPOLYTYPE) ) return LW_TRUE; if ( collectiontype == POLYHEDRALSURFACETYPE && subtype == POLYGONTYPE ) return LW_TRUE; if ( collectiontype == TINTYPE && subtype == TRIANGLETYPE ) return LW_TRUE; /* Must be a bad combination! */ return LW_FALSE; } int lwcollection_startpoint(const LWCOLLECTION* col, POINT4D* pt) { if ( col->ngeoms < 1 ) return LW_FAILURE; return lwgeom_startpoint(col->geoms[0], pt); } lwgeom/src/liblwgeom/lwpoly.c0000644000176200001440000002777113773172540016035 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic LWPOLY manipulation */ #include #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" #define CHECK_POLY_RINGS_ZM 1 /* construct a new LWPOLY. arrays (points/points per ring) will NOT be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points) { LWPOLY *result; int hasz, hasm; #ifdef CHECK_POLY_RINGS_ZM char zm; uint32_t i; #endif if ( nrings < 1 ) lwerror("lwpoly_construct: need at least 1 ring"); hasz = FLAGS_GET_Z(points[0]->flags); hasm = FLAGS_GET_M(points[0]->flags); #ifdef CHECK_POLY_RINGS_ZM zm = FLAGS_GET_ZM(points[0]->flags); for (i=1; iflags) ) lwerror("lwpoly_construct: mixed dimensioned rings"); } #endif result = (LWPOLY*) lwalloc(sizeof(LWPOLY)); result->type = POLYGONTYPE; result->flags = lwflags(hasz, hasm, 0); FLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->nrings = nrings; result->maxrings = nrings; result->rings = points; result->bbox = bbox; return result; } LWPOLY* lwpoly_construct_rectangle(char hasz, char hasm, POINT4D *p1, POINT4D *p2, POINT4D *p3, POINT4D *p4) { POINTARRAY *pa = ptarray_construct_empty(hasz, hasm, 5); LWPOLY *lwpoly = lwpoly_construct_empty(SRID_UNKNOWN, hasz, hasm); ptarray_append_point(pa, p1, LW_TRUE); ptarray_append_point(pa, p2, LW_TRUE); ptarray_append_point(pa, p3, LW_TRUE); ptarray_append_point(pa, p4, LW_TRUE); ptarray_append_point(pa, p1, LW_TRUE); lwpoly_add_ring(lwpoly, pa); return lwpoly; } LWPOLY * lwpoly_construct_envelope(int32_t srid, double x1, double y1, double x2, double y2) { POINT4D p1, p2, p3, p4; LWPOLY *poly; p1.x = x1; p1.y = y1; p2.x = x1; p2.y = y2; p3.x = x2; p3.y = y2; p4.x = x2; p4.y = y1; poly = lwpoly_construct_rectangle(0, 0, &p1, &p2, &p3, &p4); lwgeom_set_srid(lwpoly_as_lwgeom(poly), srid); lwgeom_add_bbox(lwpoly_as_lwgeom(poly)); return poly; } LWPOLY * lwpoly_construct_circle(int32_t srid, double x, double y, double radius, uint32_t segments_per_quarter, char exterior) { const uint32_t segments = 4*segments_per_quarter; double theta; LWPOLY *lwpoly; POINTARRAY *pa; POINT4D pt; uint32_t i; if (segments_per_quarter == 0) { lwerror("Need at least one segment per quarter-circle."); return NULL; } if (radius < 0) { lwerror("Radius must be positive."); return NULL; } theta = 2*M_PI / segments; lwpoly = lwpoly_construct_empty(srid, LW_FALSE, LW_FALSE); pa = ptarray_construct_empty(LW_FALSE, LW_FALSE, segments + 1); if (exterior) radius *= sqrt(1 + pow(tan(theta/2), 2)); for (i = 0; i <= segments; i++) { pt.x = x + radius*sin(i * theta); pt.y = y + radius*cos(i * theta); ptarray_append_point(pa, &pt, LW_TRUE); } lwpoly_add_ring(lwpoly, pa); return lwpoly; } LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm) { LWPOLY *result = lwalloc(sizeof(LWPOLY)); result->type = POLYGONTYPE; result->flags = lwflags(hasz,hasm,0); result->srid = srid; result->nrings = 0; result->maxrings = 1; /* Allocate room for ring, just in case. */ result->rings = lwalloc(result->maxrings * sizeof(POINTARRAY*)); result->bbox = NULL; return result; } void lwpoly_free(LWPOLY* poly) { uint32_t t; if (!poly) return; if (poly->bbox) lwfree(poly->bbox); if ( poly->rings ) { for (t = 0; t < poly->nrings; t++) if (poly->rings[t]) ptarray_free(poly->rings[t]); lwfree(poly->rings); } lwfree(poly); } void printLWPOLY(LWPOLY *poly) { uint32_t t; lwnotice("LWPOLY {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(poly->flags)); lwnotice(" SRID = %i", (int)poly->srid); lwnotice(" nrings = %i", (int)poly->nrings); for (t=0; tnrings; t++) { lwnotice(" RING # %i :",t); printPA(poly->rings[t]); } lwnotice("}"); } /* @brief Clone LWLINE object. Serialized point lists are not copied. * * @see ptarray_clone */ LWPOLY * lwpoly_clone(const LWPOLY *g) { uint32_t i; LWPOLY *ret = lwalloc(sizeof(LWPOLY)); memcpy(ret, g, sizeof(LWPOLY)); ret->rings = lwalloc(sizeof(POINTARRAY *)*g->nrings); for ( i = 0; i < g->nrings; i++ ) { ret->rings[i] = ptarray_clone(g->rings[i]); } if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); return ret; } /* Deep clone LWPOLY object. POINTARRAY are copied, as is ring array */ LWPOLY * lwpoly_clone_deep(const LWPOLY *g) { uint32_t i; LWPOLY *ret = lwalloc(sizeof(LWPOLY)); memcpy(ret, g, sizeof(LWPOLY)); if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); ret->rings = lwalloc(sizeof(POINTARRAY *)*g->nrings); for ( i = 0; i < ret->nrings; i++ ) { ret->rings[i] = ptarray_clone_deep(g->rings[i]); } FLAGS_SET_READONLY(ret->flags,0); return ret; } /** * Add a ring to a polygon. Point array will be referenced, not copied. */ int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa) { if( ! poly || ! pa ) return LW_FAILURE; /* We have used up our storage, add some more. */ if( poly->nrings >= poly->maxrings ) { int new_maxrings = 2 * (poly->nrings + 1); poly->rings = lwrealloc(poly->rings, new_maxrings * sizeof(POINTARRAY*)); poly->maxrings = new_maxrings; } /* Add the new ring entry. */ poly->rings[poly->nrings] = pa; poly->nrings++; return LW_SUCCESS; } void lwpoly_force_clockwise(LWPOLY *poly) { uint32_t i; /* No-op empties */ if ( lwpoly_is_empty(poly) ) return; /* External ring */ if ( ptarray_isccw(poly->rings[0]) ) ptarray_reverse_in_place(poly->rings[0]); /* Internal rings */ for (i=1; inrings; i++) if ( ! ptarray_isccw(poly->rings[i]) ) ptarray_reverse_in_place(poly->rings[i]); } int lwpoly_is_clockwise(LWPOLY *poly) { uint32_t i; if ( lwpoly_is_empty(poly) ) return LW_TRUE; if ( ptarray_isccw(poly->rings[0]) ) return LW_FALSE; for ( i = 1; i < poly->nrings; i++) if ( !ptarray_isccw(poly->rings[i]) ) return LW_FALSE; return LW_TRUE; } void lwpoly_release(LWPOLY *lwpoly) { lwgeom_release(lwpoly_as_lwgeom(lwpoly)); } LWPOLY * lwpoly_segmentize2d(const LWPOLY *poly, double dist) { POINTARRAY **newrings; uint32_t i; newrings = lwalloc(sizeof(POINTARRAY *)*poly->nrings); for (i=0; inrings; i++) { newrings[i] = ptarray_segmentize2d(poly->rings[i], dist); if ( ! newrings[i] ) { uint32_t j = 0; for (j = 0; j < i; j++) ptarray_free(newrings[j]); lwfree(newrings); return NULL; } } return lwpoly_construct(poly->srid, NULL, poly->nrings, newrings); } /* * check coordinate equality * ring and coordinate order is considered */ char lwpoly_same(const LWPOLY *p1, const LWPOLY *p2) { uint32_t i; if ( p1->nrings != p2->nrings ) return 0; for (i=0; inrings; i++) { if ( ! ptarray_same(p1->rings[i], p2->rings[i]) ) return 0; } return 1; } /* * Construct a polygon from a LWLINE being * the shell and an array of LWLINE (possibly NULL) being holes. * Pointarrays from intput geoms are cloned. * SRID must be the same for each input line. * Input lines must have at least 4 points, and be closed. */ LWPOLY * lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes) { uint32_t nrings; POINTARRAY **rings = lwalloc((nholes+1)*sizeof(POINTARRAY *)); int32_t srid = shell->srid; LWPOLY *ret; if ( shell->points->npoints < 4 ) lwerror("lwpoly_from_lwlines: shell must have at least 4 points"); if ( ! ptarray_is_closed_2d(shell->points) ) lwerror("lwpoly_from_lwlines: shell must be closed"); rings[0] = ptarray_clone_deep(shell->points); for (nrings=1; nrings<=nholes; nrings++) { const LWLINE *hole = holes[nrings-1]; if ( hole->srid != srid ) lwerror("lwpoly_from_lwlines: mixed SRIDs in input lines"); if ( hole->points->npoints < 4 ) lwerror("lwpoly_from_lwlines: holes must have at least 4 points"); if ( ! ptarray_is_closed_2d(hole->points) ) lwerror("lwpoly_from_lwlines: holes must be closed"); rings[nrings] = ptarray_clone_deep(hole->points); } ret = lwpoly_construct(srid, NULL, nrings, rings); return ret; } LWPOLY* lwpoly_force_dims(const LWPOLY *poly, int hasz, int hasm) { LWPOLY *polyout; /* Return 2D empty */ if( lwpoly_is_empty(poly) ) { polyout = lwpoly_construct_empty(poly->srid, hasz, hasm); } else { POINTARRAY **rings = NULL; uint32_t i; rings = lwalloc(sizeof(POINTARRAY*) * poly->nrings); for( i = 0; i < poly->nrings; i++ ) { rings[i] = ptarray_force_dims(poly->rings[i], hasz, hasm); } polyout = lwpoly_construct(poly->srid, NULL, poly->nrings, rings); } polyout->type = poly->type; return polyout; } uint32_t lwpoly_count_vertices(LWPOLY *poly) { uint32_t i = 0; uint32_t v = 0; /* vertices */ assert(poly); for ( i = 0; i < poly->nrings; i ++ ) { v += poly->rings[i]->npoints; } return v; } /** * Find the area of the outer ring - sum (area of inner rings). */ double lwpoly_area(const LWPOLY *poly) { double poly_area = 0.0; uint32_t i; if ( ! poly ) lwerror("lwpoly_area called with null polygon pointer!"); for ( i=0; i < poly->nrings; i++ ) { POINTARRAY *ring = poly->rings[i]; double ringarea = 0.0; /* Empty or messed-up ring. */ if ( ring->npoints < 3 ) continue; ringarea = fabs(ptarray_signed_area(ring)); if ( i == 0 ) /* Outer ring, positive area! */ poly_area += ringarea; else /* Inner ring, negative area! */ poly_area -= ringarea; } return poly_area; } /** * Compute the sum of polygon rings length. * Could use a more numerically stable calculator... */ double lwpoly_perimeter(const LWPOLY *poly) { double result=0.0; uint32_t i; LWDEBUGF(2, "in lwgeom_polygon_perimeter (%d rings)", poly->nrings); for (i=0; inrings; i++) result += ptarray_length(poly->rings[i]); return result; } /** * Compute the sum of polygon rings length (forcing 2d computation). * Could use a more numerically stable calculator... */ double lwpoly_perimeter_2d(const LWPOLY *poly) { double result=0.0; uint32_t i; LWDEBUGF(2, "in lwgeom_polygon_perimeter (%d rings)", poly->nrings); for (i=0; inrings; i++) result += ptarray_length_2d(poly->rings[i]); return result; } int lwpoly_is_closed(const LWPOLY *poly) { uint32_t i = 0; if ( poly->nrings == 0 ) return LW_TRUE; for ( i = 0; i < poly->nrings; i++ ) { if (FLAGS_GET_Z(poly->flags)) { if ( ! ptarray_is_closed_3d(poly->rings[i]) ) return LW_FALSE; } else { if ( ! ptarray_is_closed_2d(poly->rings[i]) ) return LW_FALSE; } } return LW_TRUE; } int lwpoly_startpoint(const LWPOLY* poly, POINT4D* pt) { if ( poly->nrings < 1 ) return LW_FAILURE; return ptarray_startpoint(poly->rings[0], pt); } int lwpoly_contains_point(const LWPOLY *poly, const POINT2D *pt) { uint32_t i; int t; if ( lwpoly_is_empty(poly) ) return LW_OUTSIDE; t = ptarray_contains_point(poly->rings[0], pt); if (t == LW_INSIDE) { for (i = 1; i < poly->nrings; i++) { t = ptarray_contains_point(poly->rings[i], pt); if (t == LW_INSIDE) return LW_OUTSIDE; if (t == LW_BOUNDARY) { return LW_BOUNDARY; } } return LW_INSIDE; } else return t; } lwgeom/src/liblwgeom/lwgeom_geos.h0000644000176200001440000000416613773172540017014 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2018 Darafei Praliaskouski * **********************************************************************/ #include "geos_c.h" #include "liblwgeom.h" #include "lwunionfind.h" /* ** Public prototypes for GEOS utility functions. */ LWGEOM* GEOS2LWGEOM(const GEOSGeometry* geom, uint8_t want3d); GEOSGeometry* LWGEOM2GEOS(const LWGEOM* g, uint8_t autofix); GEOSGeometry* GBOX2GEOS(const GBOX* g); #if POSTGIS_GEOS_VERSION < 38 GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in); GEOSGeometry* LWGEOM_GEOS_makeValid(const GEOSGeometry*); #endif GEOSGeometry* make_geos_point(double x, double y); GEOSGeometry* make_geos_segment(double x1, double y1, double x2, double y2); int cluster_intersecting(GEOSGeometry **geoms, uint32_t num_geoms, GEOSGeometry ***clusterGeoms, uint32_t *num_clusters); int cluster_within_distance(LWGEOM **geoms, uint32_t num_geoms, double tolerance, LWGEOM ***clusterGeoms, uint32_t *num_clusters); int union_dbscan(LWGEOM **geoms, uint32_t num_geoms, UNIONFIND *uf, double eps, uint32_t min_points, char **is_in_cluster_ret); POINTARRAY* ptarray_from_GEOSCoordSeq(const GEOSCoordSequence* cs, uint8_t want3d); extern char lwgeom_geos_errmsg[]; extern void lwgeom_geos_error(const char* fmt, ...); lwgeom/src/liblwgeom/lwtriangle.c0000644000176200001440000001167313773172540016651 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2010 - Oslandia * **********************************************************************/ /* basic LWTRIANGLE manipulation */ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" /* construct a new LWTRIANGLE. * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWTRIANGLE * lwtriangle_construct(int32_t srid, GBOX *bbox, POINTARRAY *points) { LWTRIANGLE *result; result = (LWTRIANGLE*) lwalloc(sizeof(LWTRIANGLE)); result->type = TRIANGLETYPE; result->flags = points->flags; FLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->points = points; result->bbox = bbox; return result; } LWTRIANGLE * lwtriangle_construct_empty(int32_t srid, char hasz, char hasm) { LWTRIANGLE *result = lwalloc(sizeof(LWTRIANGLE)); result->type = TRIANGLETYPE; result->flags = lwflags(hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(hasz, hasm, 1); result->bbox = NULL; return result; } void lwtriangle_free(LWTRIANGLE *triangle) { if ( ! triangle ) return; if (triangle->bbox) lwfree(triangle->bbox); if (triangle->points) ptarray_free(triangle->points); lwfree(triangle); } void printLWTRIANGLE(LWTRIANGLE *triangle) { if (triangle->type != TRIANGLETYPE) lwerror("printLWTRIANGLE called with something else than a Triangle"); lwnotice("LWTRIANGLE {"); lwnotice(" ndims = %i", (int)FLAGS_NDIMS(triangle->flags)); lwnotice(" SRID = %i", (int)triangle->srid); printPA(triangle->points); lwnotice("}"); } /* @brief Clone LWTRIANGLE object. Serialized point lists are not copied. * * @see ptarray_clone */ LWTRIANGLE * lwtriangle_clone(const LWTRIANGLE *g) { LWDEBUGF(2, "lwtriangle_clone called with %p", g); return (LWTRIANGLE *)lwline_clone((const LWLINE *)g); } void lwtriangle_force_clockwise(LWTRIANGLE *triangle) { if ( ptarray_isccw(triangle->points) ) ptarray_reverse_in_place(triangle->points); } int lwtriangle_is_clockwise(LWTRIANGLE *triangle) { return !ptarray_isccw(triangle->points); } void lwtriangle_release(LWTRIANGLE *lwtriangle) { lwgeom_release(lwtriangle_as_lwgeom(lwtriangle)); } /* check coordinate equality */ char lwtriangle_same(const LWTRIANGLE *t1, const LWTRIANGLE *t2) { char r = ptarray_same(t1->points, t2->points); LWDEBUGF(5, "returning %d", r); return r; } static char lwtriangle_is_repeated_points(LWTRIANGLE *triangle) { char ret; POINTARRAY *pa; pa = ptarray_remove_repeated_points(triangle->points, 0.0); ret = ptarray_same(pa, triangle->points); ptarray_free(pa); return ret; } /* * Construct a triangle from a LWLINE being * the shell * Pointarray from input geom is cloned. * Input line must have 4 points, and be closed. */ LWTRIANGLE * lwtriangle_from_lwline(const LWLINE *shell) { LWTRIANGLE *ret; POINTARRAY *pa; if ( shell->points->npoints != 4 ) lwerror("lwtriangle_from_lwline: shell must have exactly 4 points"); if ( (!FLAGS_GET_Z(shell->flags) && !ptarray_is_closed_2d(shell->points)) || (FLAGS_GET_Z(shell->flags) && !ptarray_is_closed_3d(shell->points)) ) lwerror("lwtriangle_from_lwline: shell must be closed"); pa = ptarray_clone_deep(shell->points); ret = lwtriangle_construct(shell->srid, NULL, pa); if (lwtriangle_is_repeated_points(ret)) lwerror("lwtriangle_from_lwline: some points are repeated in triangle"); return ret; } /** * Find the area of the outer ring */ double lwtriangle_area(const LWTRIANGLE *triangle) { double area=0.0; uint32_t i; POINT2D p1; POINT2D p2; if (! triangle->points->npoints) return area; /* empty triangle */ for (i=0; i < triangle->points->npoints-1; i++) { getPoint2d_p(triangle->points, i, &p1); getPoint2d_p(triangle->points, i+1, &p2); area += ( p1.x * p2.y ) - ( p1.y * p2.x ); } area /= 2.0; return fabs(area); } double lwtriangle_perimeter(const LWTRIANGLE *triangle) { if( triangle->points ) return ptarray_length(triangle->points); else return 0.0; } double lwtriangle_perimeter_2d(const LWTRIANGLE *triangle) { if( triangle->points ) return ptarray_length_2d(triangle->points); else return 0.0; } lwgeom/src/liblwgeom/lookup3.c0000644000176200001440000006627513773172540016105 0ustar liggesusers/* ------------------------------------------------------------------------------- lookup3.c, by Bob Jenkins, May 2006, Public Domain. These are functions for producing 32-bit hashes for hash table lookup. hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are externally useful functions. Routines to test the hash are included if SELF_TEST is defined. You can use this free for any purpose. It's in the public domain. It has no warranty. You probably want to use hashlittle(). hashlittle() and hashbig() hash byte arrays. hashlittle() is is faster than hashbig() on little-endian machines. Intel and AMD are little-endian machines. On second thought, you probably want hashlittle2(), which is identical to hashlittle() except it returns two 32-bit hashes for the price of one. You could implement hashbig2() if you wanted but I haven't bothered here. If you want to find a hash of, say, exactly 7 integers, do a = i1; b = i2; c = i3; mix(a,b,c); a += i4; b += i5; c += i6; mix(a,b,c); a += i7; final(a,b,c); then use c as the hash value. If you have a variable length array of 4-byte integers to hash, use hashword(). If you have a byte array (like a character string), use hashlittle(). If you have several byte arrays, or a mix of things, see the comments above hashlittle(). Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then mix those integers. This is fast (you can do a lot more thorough mixing with 12*3 instructions on 3 integers than you can with 3 instructions on 1 byte), but shoehorning those bytes into integers efficiently is messy. ------------------------------------------------------------------------------- */ #define SELF_TEST 1 #include /* defines printf for tests */ #include /* defines time_t for timings in the test */ #include /* defines uint32_t etc */ #include /* attempt to define endianness */ #ifdef linux # include /* attempt to define endianness */ #endif /* * My best guess at if you are big-endian or little-endian. This may * need adjustment. */ #if (defined(WORDS_BIGENDIAN)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #elif (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN) || \ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 #endif #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) #if 0 static uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval); static void hashword2 (const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); static uint32_t hashlittle( const void *key, size_t length, uint32_t initval); static uint32_t hashbig( const void *key, size_t length, uint32_t initval); #endif void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. This is reversible, so any information in (a,b,c) before mix() is still in (a,b,c) after mix(). If four pairs of (a,b,c) inputs are run through mix(), or through mix() in reverse, there are at least 32 bits of the output that are sometimes the same for one pair and different for another pair. This was tested for: * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this are 4 6 8 16 19 4 9 15 3 18 27 15 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) that fail to affect some output bits of (a,b,c), especially of a. The most thoroughly mixed value is c, but it doesn't really even achieve avalanche in c. This allows some parallelism. Read-after-writes are good at doubling the number of bits affected, so the goal of mixing pulls in the opposite direction as the goal of parallelism. I did what I could. Rotates seem to cost as much as shifts on every machine I could lay my hands on, and rotates are much kinder to the top and bottom bits, so I used rotates. ------------------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* ------------------------------------------------------------------------------- final -- final mixing of 3 32-bit values (a,b,c) into c Pairs of (a,b,c) values differing in only a few bits will usually produce values of c that look totally different. This was tested for * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: 14 11 25 16 4 14 24 12 14 25 16 4 14 24 and these came close: 4 8 15 26 3 22 24 10 8 15 26 3 22 24 11 8 15 26 3 22 24 ------------------------------------------------------------------------------- */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } #if 0 /* -------------------------------------------------------------------- This works on all machines. To be useful, it requires -- that the key be an array of uint32_t's, and -- that the length be the number of uint32_t's in the key The function hashword() is identical to hashlittle() on little-endian machines, and identical to hashbig() on big-endian machines, except that the length has to be measured in uint32_ts rather than in bytes. hashlittle() is more complicated than hashword() only because hashlittle() has to dance around fitting the key bytes into registers. -------------------------------------------------------------------- */ static uint32_t hashword( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t initval) /* the previous hash, or an arbitrary value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ return c; } #endif #if 0 /* -------------------------------------------------------------------- hashword2() -- same as hashword(), but take two seeds and return two 32-bit values. pc and pb must both be nonnull, and *pc and *pb must both be initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be the same as the return value from hashword(). -------------------------------------------------------------------- */ static void hashword2 ( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t *pc, /* IN: seed OUT: primary hash value */ uint32_t *pb) /* IN: more seed OUT: secondary hash value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; c += *pb; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ *pc=c; *pb=b; } #endif /* ------------------------------------------------------------------------------- hashlittle() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) length : the length of the key, counting by bytes initval : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Two keys differing by one or two bits will have totally different hash values. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; i 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : return c; } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : return c; /* zero length requires no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : return c; } } final(a,b,c); return c; } #endif /* * hashlittle2: return 2 32-bit hash values * * This is identical to hashlittle(), except it returns two 32-bit hash * values instead of just one. This is good enough for hash table * lookup with 2^^64 buckets, or if you want a second hash if you're not * happy with the first, or if you want a probably-unique 64-bit ID for * the key. *pc is better mixed than *pb, so use *pc first. If you want * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". */ void hashlittle2( const void *key, /* the key to hash */ size_t length, /* length of the key */ uint32_t *pc, /* IN: primary initval, OUT: primary hash */ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ { uint32_t a,b,c; /* internal state */ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; c += *pb; u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ // const uint8_t *k8; /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; /* fall through */ case 11: c+=((uint32_t)k[10])<<16; /* fall through */ case 10: c+=((uint32_t)k[9])<<8; /* fall through */ case 9 : c+=k[8]; /* fall through */ case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ case 5 : b+=k[4]; /* fall through */ case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ case 1 : a+=k[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } final(a,b,c); *pc=c; *pb=b; } #if 0 /* * hashbig(): * This is the same as hashword() on big-endian machines. It is different * from hashlittle() on all machines. hashbig() takes advantage of * big-endian byte ordering. */ static uint32_t hashbig( const void *key, size_t length, uint32_t initval) { uint32_t a,b,c; union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; u.ptr = key; if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ // const uint8_t *k8; /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]<<8" actually reads beyond the end of the string, but * then shifts out the part it's not allowed to read. Because the * string is aligned, the illegal read is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; case 5 : b+=k[1]&0xff000000; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff00; break; case 2 : a+=k[0]&0xffff0000; break; case 1 : a+=k[0]&0xff000000; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) /* all the case statements fall through */ { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ case 1 : a+=((uint32_t)k8[0])<<24; break; case 0 : return c; } #endif /* !VALGRIND */ } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += ((uint32_t)k[0])<<24; a += ((uint32_t)k[1])<<16; a += ((uint32_t)k[2])<<8; a += ((uint32_t)k[3]); b += ((uint32_t)k[4])<<24; b += ((uint32_t)k[5])<<16; b += ((uint32_t)k[6])<<8; b += ((uint32_t)k[7]); c += ((uint32_t)k[8])<<24; c += ((uint32_t)k[9])<<16; c += ((uint32_t)k[10])<<8; c += ((uint32_t)k[11]); mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=k[11]; case 11: c+=((uint32_t)k[10])<<8; case 10: c+=((uint32_t)k[9])<<16; case 9 : c+=((uint32_t)k[8])<<24; case 8 : b+=k[7]; case 7 : b+=((uint32_t)k[6])<<8; case 6 : b+=((uint32_t)k[5])<<16; case 5 : b+=((uint32_t)k[4])<<24; case 4 : a+=k[3]; case 3 : a+=((uint32_t)k[2])<<8; case 2 : a+=((uint32_t)k[1])<<16; case 1 : a+=((uint32_t)k[0])<<24; break; case 0 : return c; } } final(a,b,c); return c; } #endif lwgeom/src/liblwgeom/lwin_wkt.c0000644000176200001440000005270313773172540016336 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2010 Paul Ramsey * **********************************************************************/ #include #include /* for isspace */ #include "lwin_wkt.h" #include "lwin_wkt_parse.h" #include "lwgeom_log.h" /* * Error messages for failures in the parser. */ const char *parser_error_messages[] = { "", "geometry requires more points", "geometry must have an odd number of points", "geometry contains non-closed rings", "can not mix dimensionality in a geometry", "parse error - invalid geometry", "invalid WKB type", "incontinuous compound curve", "triangle must have exactly 4 points", "geometry has too many points", "parse error - invalid geometry" }; #define SET_PARSER_ERROR(errno) { \ global_parser_result.message = parser_error_messages[(errno)]; \ global_parser_result.errcode = (errno); \ global_parser_result.errlocation = wkt_yylloc.last_column; \ } /** * Read the SRID number from an SRID=<> string */ int wkt_lexer_read_srid(char *str) { char *c = str; long i = 0; int32_t srid; if( ! str ) return SRID_UNKNOWN; c += 5; /* Advance past "SRID=" */ i = strtol(c, NULL, 10); srid = clamp_srid((int32_t)i); /* TODO: warn on explicit UNKNOWN srid ? */ return srid; } static lwflags_t wkt_dimensionality(char *dimensionality) { size_t i = 0; lwflags_t flags = 0; if( ! dimensionality ) return flags; /* If there's an explicit dimensionality, we use that */ for( i = 0; i < strlen(dimensionality); i++ ) { if( (dimensionality[i] == 'Z') || (dimensionality[i] == 'z') ) FLAGS_SET_Z(flags,1); else if( (dimensionality[i] == 'M') || (dimensionality[i] == 'm') ) FLAGS_SET_M(flags,1); /* only a space is accepted in between */ else if( ! isspace(dimensionality[i]) ) break; } return flags; } /** * Force the dimensionality of a geometry to match the dimensionality * of a set of flags (usually derived from a ZM WKT tag). */ static int wkt_parser_set_dims(LWGEOM *geom, lwflags_t flags) { int hasz = FLAGS_GET_Z(flags); int hasm = FLAGS_GET_M(flags); uint32_t i = 0; /* Error on junk */ if( ! geom ) return LW_FAILURE; FLAGS_SET_Z(geom->flags, hasz); FLAGS_SET_M(geom->flags, hasm); switch( geom->type ) { case POINTTYPE: { LWPOINT *pt = (LWPOINT*)geom; if ( pt->point ) { FLAGS_SET_Z(pt->point->flags, hasz); FLAGS_SET_M(pt->point->flags, hasm); } break; } case TRIANGLETYPE: case CIRCSTRINGTYPE: case LINETYPE: { LWLINE *ln = (LWLINE*)geom; if ( ln->points ) { FLAGS_SET_Z(ln->points->flags, hasz); FLAGS_SET_M(ln->points->flags, hasm); } break; } case POLYGONTYPE: { LWPOLY *poly = (LWPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { if( poly->rings[i] ) { FLAGS_SET_Z(poly->rings[i]->flags, hasz); FLAGS_SET_M(poly->rings[i]->flags, hasm); } } break; } case CURVEPOLYTYPE: { LWCURVEPOLY *poly = (LWCURVEPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) wkt_parser_set_dims(poly->rings[i], flags); break; } default: { if ( lwtype_is_collection(geom->type) ) { LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) wkt_parser_set_dims(col->geoms[i], flags); return LW_SUCCESS; } else { LWDEBUGF(2,"Unknown geometry type: %d", geom->type); return LW_FAILURE; } } } return LW_SUCCESS; } /** * Read the dimensionality from a flag, if provided. Then check that the * dimensionality matches that of the pointarray. If the dimension counts * match, ensure the pointarray is using the right "Z" or "M". */ static int wkt_pointarray_dimensionality(POINTARRAY *pa, lwflags_t flags) { int hasz = FLAGS_GET_Z(flags); int hasm = FLAGS_GET_M(flags); int ndims = 2 + hasz + hasm; /* No dimensionality or array means we go with what we have */ if( ! (flags && pa) ) return LW_TRUE; LWDEBUGF(5,"dimensionality ndims == %d", ndims); LWDEBUGF(5,"FLAGS_NDIMS(pa->flags) == %d", FLAGS_NDIMS(pa->flags)); /* * ndims > 2 implies that the flags have something useful to add, * that there is a 'Z' or an 'M' or both. */ if( ndims > 2 ) { /* Mismatch implies a problem */ if ( FLAGS_NDIMS(pa->flags) != ndims ) return LW_FALSE; /* Match means use the explicit dimensionality */ else { FLAGS_SET_Z(pa->flags, hasz); FLAGS_SET_M(pa->flags, hasm); } } return LW_TRUE; } /** * Build a 2d coordinate. */ POINT wkt_parser_coord_2(double c1, double c2) { POINT p; p.flags = 0; p.x = c1; p.y = c2; p.z = p.m = 0.0; FLAGS_SET_Z(p.flags, 0); FLAGS_SET_M(p.flags, 0); return p; } /** * Note, if this is an XYM coordinate we'll have to fix it later when we build * the object itself and have access to the dimensionality token. */ POINT wkt_parser_coord_3(double c1, double c2, double c3) { POINT p; p.flags = 0; p.x = c1; p.y = c2; p.z = c3; p.m = 0; FLAGS_SET_Z(p.flags, 1); FLAGS_SET_M(p.flags, 0); return p; } /** */ POINT wkt_parser_coord_4(double c1, double c2, double c3, double c4) { POINT p; p.flags = 0; p.x = c1; p.y = c2; p.z = c3; p.m = c4; FLAGS_SET_Z(p.flags, 1); FLAGS_SET_M(p.flags, 1); return p; } POINTARRAY* wkt_parser_ptarray_add_coord(POINTARRAY *pa, POINT p) { POINT4D pt; LWDEBUG(4,"entered"); /* Error on trouble */ if( ! pa ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* Check that the coordinate has the same dimesionality as the array */ if( FLAGS_NDIMS(p.flags) != FLAGS_NDIMS(pa->flags) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* While parsing the point arrays, XYM and XMZ points are both treated as XYZ */ pt.x = p.x; pt.y = p.y; if( FLAGS_GET_Z(pa->flags) ) pt.z = p.z; if( FLAGS_GET_M(pa->flags) ) pt.m = p.m; /* If the destination is XYM, we'll write the third coordinate to m */ if( FLAGS_GET_M(pa->flags) && ! FLAGS_GET_Z(pa->flags) ) pt.m = p.z; ptarray_append_point(pa, &pt, LW_TRUE); /* Allow duplicate points in array */ return pa; } /** * Start a point array from the first coordinate. */ POINTARRAY* wkt_parser_ptarray_new(POINT p) { int ndims = FLAGS_NDIMS(p.flags); POINTARRAY *pa = ptarray_construct_empty((ndims>2), (ndims>3), 4); LWDEBUG(4,"entered"); if ( ! pa ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } return wkt_parser_ptarray_add_coord(pa, p); } /** * Create a new point. Null point array implies empty. Null dimensionality * implies no specified dimensionality in the WKT. */ LWGEOM* wkt_parser_point_new(POINTARRAY *pa, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); LWDEBUG(4,"entered"); /* No pointarray means it is empty */ if( ! pa ) return lwpoint_as_lwgeom(lwpoint_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); /* If the number of dimensions is not consistent, we have a problem. */ if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Only one point allowed in our point array! */ if( pa->npoints != 1 ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_LESSPOINTS); return NULL; } return lwpoint_as_lwgeom(lwpoint_construct(SRID_UNKNOWN, NULL, pa)); } /** * Create a new linestring. Null point array implies empty. Null dimensionality * implies no specified dimensionality in the WKT. Check for numpoints >= 2 if * requested. */ LWGEOM* wkt_parser_linestring_new(POINTARRAY *pa, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); LWDEBUG(4,"entered"); /* No pointarray means it is empty */ if( ! pa ) return lwline_as_lwgeom(lwline_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); /* If the number of dimensions is not consistent, we have a problem. */ if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Apply check for not enough points, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 2) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS); return NULL; } return lwline_as_lwgeom(lwline_construct(SRID_UNKNOWN, NULL, pa)); } /** * Create a new circularstring. Null point array implies empty. Null dimensionality * implies no specified dimensionality in the WKT. * Circular strings are just like linestrings, except with slighty different * validity rules (minpoint == 3, numpoints % 2 == 1). */ LWGEOM* wkt_parser_circularstring_new(POINTARRAY *pa, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); LWDEBUG(4,"entered"); /* No pointarray means it is empty */ if( ! pa ) return lwcircstring_as_lwgeom(lwcircstring_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); /* If the number of dimensions is not consistent, we have a problem. */ if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Apply check for not enough points, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 3) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS); return NULL; } /* Apply check for odd number of points, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_ODD) && ((pa->npoints % 2) == 0) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_ODDPOINTS); return NULL; } return lwcircstring_as_lwgeom(lwcircstring_construct(SRID_UNKNOWN, NULL, pa)); } LWGEOM* wkt_parser_triangle_new(POINTARRAY *pa, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); LWDEBUG(4,"entered"); /* No pointarray means it is empty */ if( ! pa ) return lwtriangle_as_lwgeom(lwtriangle_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); /* If the number of dimensions is not consistent, we have a problem. */ if( wkt_pointarray_dimensionality(pa, flags) == LW_FALSE ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Triangles need four points. */ if( (pa->npoints != 4) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_TRIANGLEPOINTS); return NULL; } /* Triangles need closure. */ if( ! ptarray_is_closed_z(pa) ) { ptarray_free(pa); SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED); return NULL; } return lwtriangle_as_lwgeom(lwtriangle_construct(SRID_UNKNOWN, NULL, pa)); } LWGEOM* wkt_parser_polygon_new(POINTARRAY *pa, char dimcheck) { LWPOLY *poly = NULL; LWDEBUG(4,"entered"); /* No pointarray is a problem */ if( ! pa ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } poly = lwpoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags)); /* Error out if we can't build this polygon. */ if( ! poly ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } wkt_parser_polygon_add_ring(lwpoly_as_lwgeom(poly), pa, dimcheck); return lwpoly_as_lwgeom(poly); } LWGEOM* wkt_parser_polygon_add_ring(LWGEOM *poly, POINTARRAY *pa, char dimcheck) { LWDEBUG(4,"entered"); /* Bad inputs are a problem */ if( ! (pa && poly) ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* Rings must agree on dimensionality */ if( FLAGS_NDIMS(poly->flags) != FLAGS_NDIMS(pa->flags) ) { ptarray_free(pa); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Apply check for minimum number of points, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) && (pa->npoints < 4) ) { ptarray_free(pa); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS); return NULL; } /* Apply check for not closed rings, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_CLOSURE) && ! (dimcheck == 'Z' ? ptarray_is_closed_z(pa) : ptarray_is_closed_2d(pa)) ) { ptarray_free(pa); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED); return NULL; } /* If something goes wrong adding a ring, error out. */ if ( LW_FAILURE == lwpoly_add_ring(lwgeom_as_lwpoly(poly), pa) ) { ptarray_free(pa); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } return poly; } LWGEOM* wkt_parser_polygon_finalize(LWGEOM *poly, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); int flagdims = FLAGS_NDIMS(flags); LWDEBUG(4,"entered"); /* Null input implies empty return */ if( ! poly ) return lwpoly_as_lwgeom(lwpoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); /* If the number of dimensions are not consistent, we have a problem. */ if( flagdims > 2 ) { if ( flagdims != FLAGS_NDIMS(poly->flags) ) { lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Harmonize the flags in the sub-components with the wkt flags */ if( LW_FAILURE == wkt_parser_set_dims(poly, flags) ) { lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } } return poly; } LWGEOM* wkt_parser_curvepolygon_new(LWGEOM *ring) { LWGEOM *poly; LWDEBUG(4,"entered"); /* Toss error on null geometry input */ if( ! ring ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* Construct poly and add the ring. */ poly = lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(ring->flags), FLAGS_GET_M(ring->flags))); /* Return the result. */ return wkt_parser_curvepolygon_add_ring(poly,ring); } LWGEOM* wkt_parser_curvepolygon_add_ring(LWGEOM *poly, LWGEOM *ring) { LWDEBUG(4,"entered"); /* Toss error on null input */ if( ! (ring && poly) ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); LWDEBUG(4,"inputs are null"); return NULL; } /* All the elements must agree on dimensionality */ if( FLAGS_NDIMS(poly->flags) != FLAGS_NDIMS(ring->flags) ) { LWDEBUG(4,"dimensionality does not match"); lwgeom_free(ring); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Apply check for minimum number of points, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_MINPOINTS) ) { uint32_t vertices_needed = 3; if ( ring->type == LINETYPE ) vertices_needed = 4; if (lwgeom_count_vertices(ring) < vertices_needed) { LWDEBUG(4,"number of points is incorrect"); lwgeom_free(ring); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MOREPOINTS); return NULL; } } /* Apply check for not closed rings, if requested. */ if( (global_parser_result.parser_check_flags & LW_PARSER_CHECK_CLOSURE) ) { int is_closed = 1; LWDEBUG(4,"checking ring closure"); switch ( ring->type ) { case LINETYPE: is_closed = lwline_is_closed(lwgeom_as_lwline(ring)); break; case CIRCSTRINGTYPE: is_closed = lwcircstring_is_closed(lwgeom_as_lwcircstring(ring)); break; case COMPOUNDTYPE: is_closed = lwcompound_is_closed(lwgeom_as_lwcompound(ring)); break; } if ( ! is_closed ) { LWDEBUG(4,"ring is not closed"); lwgeom_free(ring); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_UNCLOSED); return NULL; } } if( LW_FAILURE == lwcurvepoly_add_ring(lwgeom_as_lwcurvepoly(poly), ring) ) { LWDEBUG(4,"failed to add ring"); lwgeom_free(ring); lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } return poly; } LWGEOM* wkt_parser_curvepolygon_finalize(LWGEOM *poly, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); int flagdims = FLAGS_NDIMS(flags); LWDEBUG(4,"entered"); /* Null input implies empty return */ if( ! poly ) return lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); if ( flagdims > 2 ) { /* If the number of dimensions are not consistent, we have a problem. */ if( flagdims != FLAGS_NDIMS(poly->flags) ) { lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } /* Harmonize the flags in the sub-components with the wkt flags */ if( LW_FAILURE == wkt_parser_set_dims(poly, flags) ) { lwgeom_free(poly); SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } } return poly; } LWGEOM* wkt_parser_collection_new(LWGEOM *geom) { LWCOLLECTION *col; LWGEOM **geoms; static int ngeoms = 1; LWDEBUG(4,"entered"); /* Toss error on null geometry input */ if( ! geom ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* Create our geometry array */ geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); geoms[0] = geom; /* Make a new collection */ col = lwcollection_construct(COLLECTIONTYPE, SRID_UNKNOWN, NULL, ngeoms, geoms); /* Return the result. */ return lwcollection_as_lwgeom(col); } LWGEOM* wkt_parser_compound_new(LWGEOM *geom) { LWCOLLECTION *col; LWGEOM **geoms; static int ngeoms = 1; LWDEBUG(4,"entered"); /* Toss error on null geometry input */ if( ! geom ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* Elements of a compoundcurve cannot be empty, because */ /* empty things can't join up and form a ring */ if ( lwgeom_is_empty(geom) ) { lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_INCONTINUOUS); return NULL; } /* Create our geometry array */ geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); geoms[0] = geom; /* Make a new collection */ col = lwcollection_construct(COLLECTIONTYPE, SRID_UNKNOWN, NULL, ngeoms, geoms); /* Return the result. */ return lwcollection_as_lwgeom(col); } LWGEOM* wkt_parser_compound_add_geom(LWGEOM *col, LWGEOM *geom) { LWDEBUG(4,"entered"); /* Toss error on null geometry input */ if( ! (geom && col) ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } /* All the elements must agree on dimensionality */ if( FLAGS_NDIMS(col->flags) != FLAGS_NDIMS(geom->flags) ) { lwgeom_free(col); lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } if( LW_FAILURE == lwcompound_add_lwgeom((LWCOMPOUND*)col, geom) ) { lwgeom_free(col); lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_INCONTINUOUS); return NULL; } return col; } LWGEOM* wkt_parser_collection_add_geom(LWGEOM *col, LWGEOM *geom) { LWDEBUG(4,"entered"); /* Toss error on null geometry input */ if( ! (geom && col) ) { SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } return lwcollection_as_lwgeom(lwcollection_add_lwgeom(lwgeom_as_lwcollection(col), geom)); } LWGEOM* wkt_parser_collection_finalize(int lwtype, LWGEOM *geom, char *dimensionality) { lwflags_t flags = wkt_dimensionality(dimensionality); int flagdims = FLAGS_NDIMS(flags); /* No geometry means it is empty */ if( ! geom ) { return lwcollection_as_lwgeom(lwcollection_construct_empty(lwtype, SRID_UNKNOWN, FLAGS_GET_Z(flags), FLAGS_GET_M(flags))); } /* There are 'Z' or 'M' tokens in the signature */ if ( flagdims > 2 ) { LWCOLLECTION *col = lwgeom_as_lwcollection(geom); uint32_t i; for ( i = 0 ; i < col->ngeoms; i++ ) { LWGEOM *subgeom = col->geoms[i]; if ( FLAGS_NDIMS(flags) != FLAGS_NDIMS(subgeom->flags) && ! lwgeom_is_empty(subgeom) ) { lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } if ( lwtype == COLLECTIONTYPE && ( (FLAGS_GET_Z(flags) != FLAGS_GET_Z(subgeom->flags)) || (FLAGS_GET_M(flags) != FLAGS_GET_M(subgeom->flags)) ) && ! lwgeom_is_empty(subgeom) ) { lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS); return NULL; } } /* Harmonize the collection dimensionality */ if( LW_FAILURE == wkt_parser_set_dims(geom, flags) ) { lwgeom_free(geom); SET_PARSER_ERROR(PARSER_ERROR_OTHER); return NULL; } } /* Set the collection type */ geom->type = lwtype; return geom; } void wkt_parser_geometry_new(LWGEOM *geom, int32_t srid) { LWDEBUG(4,"entered"); LWDEBUGF(4,"geom %p",geom); LWDEBUGF(4,"srid %d",srid); if ( geom == NULL ) { lwerror("Parsed geometry is null!"); return; } if ( srid != SRID_UNKNOWN && srid < SRID_MAXIMUM ) lwgeom_set_srid(geom, srid); else lwgeom_set_srid(geom, SRID_UNKNOWN); global_parser_result.geom = geom; } void lwgeom_parser_result_init(LWGEOM_PARSER_RESULT *parser_result) { memset(parser_result, 0, sizeof(LWGEOM_PARSER_RESULT)); } void lwgeom_parser_result_free(LWGEOM_PARSER_RESULT *parser_result) { if ( parser_result->geom ) { lwgeom_free(parser_result->geom); parser_result->geom = 0; } if ( parser_result->serialized_lwgeom ) { lwfree(parser_result->serialized_lwgeom ); parser_result->serialized_lwgeom = 0; } /* We don't free parser_result->message because it is a const *char */ } /* * Public function used for easy access to the parser. */ LWGEOM *lwgeom_from_wkt(const char *wkt, const char check) { LWGEOM_PARSER_RESULT r; if( LW_FAILURE == lwgeom_parse_wkt(&r, (char*)wkt, check) ) { lwerror(r.message); return NULL; } return r.geom; } lwgeom/src/liblwgeom/stringbuffer.h0000644000176200001440000000605313773172540017202 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2002 Thamer Alharbash * Copyright 2009 Paul Ramsey * **********************************************************************/ #ifndef _STRINGBUFFER_H #define _STRINGBUFFER_H 1 #include "liblwgeom_internal.h" #include #include #include #include #define STRINGBUFFER_STARTSIZE 128 typedef struct { size_t capacity; char *str_end; char *str_start; } stringbuffer_t; extern stringbuffer_t *stringbuffer_create_with_size(size_t size); extern stringbuffer_t *stringbuffer_create(void); extern void stringbuffer_init(stringbuffer_t *s); extern void stringbuffer_release(stringbuffer_t *s); extern void stringbuffer_destroy(stringbuffer_t *sb); extern void stringbuffer_clear(stringbuffer_t *sb); void stringbuffer_set(stringbuffer_t *sb, const char *s); void stringbuffer_copy(stringbuffer_t *sb, stringbuffer_t *src); extern int stringbuffer_aprintf(stringbuffer_t *sb, const char *fmt, ...); extern const char *stringbuffer_getstring(stringbuffer_t *sb); extern char *stringbuffer_getstringcopy(stringbuffer_t *sb); extern int stringbuffer_getlength(stringbuffer_t *sb); extern char stringbuffer_lastchar(stringbuffer_t *s); extern int stringbuffer_trim_trailing_white(stringbuffer_t *s); extern int stringbuffer_trim_trailing_zeroes(stringbuffer_t *s); /** * If necessary, expand the stringbuffer_t internal buffer to accommodate the * specified additional size. */ static inline void stringbuffer_makeroom(stringbuffer_t *s, size_t size_to_add) { size_t current_size = (s->str_end - s->str_start); size_t capacity = s->capacity; size_t required_size = current_size + size_to_add; while (capacity < required_size) capacity *= 2; if (capacity > s->capacity) { s->str_start = lwrealloc(s->str_start, capacity); s->capacity = capacity; s->str_end = s->str_start + current_size; } } /** * Append the specified string to the stringbuffer_t. */ inline static void stringbuffer_append(stringbuffer_t *s, const char *a) { int alen = strlen(a); /* Length of string to append */ int alen0 = alen + 1; /* Length including null terminator */ stringbuffer_makeroom(s, alen0); memcpy(s->str_end, a, alen0); s->str_end += alen; } #endif /* _STRINGBUFFER_H */ lwgeom/src/liblwgeom/lwrandom.h0000644000176200001440000000225513773172540016325 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2019 Mike Taves * **********************************************************************/ #include #include void lwrandom_set_seed(int32_t seed); double lwrandom_uniform(void); /* for low-level external debugging */ void _lwrandom_set_seeds(int32_t s1, int32_t s2); int32_t _lwrandom_get_seed(size_t idx); lwgeom/src/liblwgeom/lwout_svg.c0000644000176200001440000003464114343213170016517 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2003 Refractions Research Inc. * **********************************************************************/ /** @file * * SVG output routines. * Originally written by: Klaus F�rster * Refactored by: Olivier Courtin (Camptocamp) * * BNF SVG Path: **********************************************************************/ #include "liblwgeom_internal.h" static char * assvg_point(const LWPOINT *point, int relative, int precision); static char * assvg_line(const LWLINE *line, int relative, int precision); static char * assvg_polygon(const LWPOLY *poly, int relative, int precision); static char * assvg_multipoint(const LWMPOINT *mpoint, int relative, int precision); static char * assvg_multiline(const LWMLINE *mline, int relative, int precision); static char * assvg_multipolygon(const LWMPOLY *mpoly, int relative, int precision); static char * assvg_collection(const LWCOLLECTION *col, int relative, int precision); static size_t assvg_geom_size(const LWGEOM *geom, int relative, int precision); static size_t assvg_geom_buf(const LWGEOM *geom, char *output, int relative, int precision); static size_t pointArray_svg_size(POINTARRAY *pa, int precision); static size_t pointArray_svg_rel(POINTARRAY *pa, char * output, int close_ring, int precision); static size_t pointArray_svg_abs(POINTARRAY *pa, char * output, int close_ring, int precision); /** * Takes a GEOMETRY and returns a SVG representation */ char * lwgeom_to_svg(const LWGEOM *geom, int precision, int relative) { char *ret = NULL; int type = geom->type; /* Empty string for empties */ if( lwgeom_is_empty(geom) ) { ret = lwalloc(1); ret[0] = '\0'; return ret; } switch (type) { case POINTTYPE: ret = assvg_point((LWPOINT*)geom, relative, precision); break; case LINETYPE: ret = assvg_line((LWLINE*)geom, relative, precision); break; case POLYGONTYPE: ret = assvg_polygon((LWPOLY*)geom, relative, precision); break; case MULTIPOINTTYPE: ret = assvg_multipoint((LWMPOINT*)geom, relative, precision); break; case MULTILINETYPE: ret = assvg_multiline((LWMLINE*)geom, relative, precision); break; case MULTIPOLYGONTYPE: ret = assvg_multipolygon((LWMPOLY*)geom, relative, precision); break; case COLLECTIONTYPE: ret = assvg_collection((LWCOLLECTION*)geom, relative, precision); break; default: lwerror("lwgeom_to_svg: '%s' geometry type not supported", lwtype_name(type)); } return ret; } /** * Point Geometry */ static size_t assvg_point_size(__attribute__((__unused__)) const LWPOINT *point, int circle, int precision) { size_t size; size = (OUT_MAX_DIGS_DOUBLE + precision) * 2; if (circle) size += sizeof("cx='' cy=''"); else size += sizeof("x='' y=''"); return size; } static size_t assvg_point_buf(const LWPOINT *point, char * output, int circle, int precision) { char *ptr=output; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; POINT2D pt; getPoint2d_p(point->point, 0, &pt); lwprint_double(pt.x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double(-pt.y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if (circle) ptr += snprintf(ptr, strlen(ptr), "x=\"%s\" y=\"%s\"", x, y); else ptr += snprintf(ptr, strlen(ptr), "cx=\"%s\" cy=\"%s\"", x, y); return (ptr-output); } static char * assvg_point(const LWPOINT *point, int circle, int precision) { char *output; int size; size = assvg_point_size(point, circle, precision); output = lwalloc(size); assvg_point_buf(point, output, circle, precision); return output; } /** * Line Geometry */ static size_t assvg_line_size(const LWLINE *line, __attribute__((__unused__)) int relative, int precision) { size_t size; size = sizeof("M "); size += pointArray_svg_size(line->points, precision); return size; } static size_t assvg_line_buf(const LWLINE *line, char * output, int relative, int precision) { char *ptr=output; /* Start path with SVG MoveTo */ ptr += snprintf(ptr, strlen(ptr), "M "); if (relative) ptr += pointArray_svg_rel(line->points, ptr, 1, precision); else ptr += pointArray_svg_abs(line->points, ptr, 1, precision); return (ptr-output); } static char * assvg_line(const LWLINE *line, int relative, int precision) { char *output; int size; size = assvg_line_size(line, relative, precision); output = lwalloc(size); assvg_line_buf(line, output, relative, precision); return output; } /** * Polygon Geometry */ static size_t assvg_polygon_size(const LWPOLY *poly, __attribute__((__unused__)) int relative, int precision) { uint32_t i; size_t size=0; for (i=0; inrings; i++) size += pointArray_svg_size(poly->rings[i], precision) + sizeof(" "); size += sizeof("M Z") * poly->nrings; return size; } static size_t assvg_polygon_buf(const LWPOLY *poly, char * output, int relative, int precision) { uint32_t i; char *ptr=output; for (i=0; inrings; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), " "); /* Space beetween each ring */ ptr += snprintf(ptr, strlen(ptr), "M "); /* Start path with SVG MoveTo */ if (relative) { ptr += pointArray_svg_rel(poly->rings[i], ptr, 0, precision); ptr += snprintf(ptr, strlen(ptr), " z"); /* SVG closepath */ } else { ptr += pointArray_svg_abs(poly->rings[i], ptr, 0, precision); ptr += snprintf(ptr, strlen(ptr), " Z"); /* SVG closepath */ } } return (ptr-output); } static char * assvg_polygon(const LWPOLY *poly, int relative, int precision) { char *output; int size; size = assvg_polygon_size(poly, relative, precision); output = lwalloc(size); assvg_polygon_buf(poly, output, relative, precision); return output; } /** * Multipoint Geometry */ static size_t assvg_multipoint_size(const LWMPOINT *mpoint, int relative, int precision) { const LWPOINT *point; size_t size=0; uint32_t i; for (i=0 ; ingeoms ; i++) { point = mpoint->geoms[i]; size += assvg_point_size(point, relative, precision); } size += sizeof(",") * --i; /* Arbitrary comma separator */ return size; } static size_t assvg_multipoint_buf(const LWMPOINT *mpoint, char *output, int relative, int precision) { const LWPOINT *point; uint32_t i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ","); /* Arbitrary comma separator */ point = mpoint->geoms[i]; ptr += assvg_point_buf(point, ptr, relative, precision); } return (ptr-output); } static char * assvg_multipoint(const LWMPOINT *mpoint, int relative, int precision) { char *output; int size; size = assvg_multipoint_size(mpoint, relative, precision); output = lwalloc(size); assvg_multipoint_buf(mpoint, output, relative, precision); return output; } /** * Multiline Geometry */ static size_t assvg_multiline_size(const LWMLINE *mline, int relative, int precision) { const LWLINE *line; size_t size=0; uint32_t i; for (i=0 ; ingeoms ; i++) { line = mline->geoms[i]; size += assvg_line_size(line, relative, precision); } size += sizeof(" ") * --i; /* SVG whitespace Separator */ return size; } static size_t assvg_multiline_buf(const LWMLINE *mline, char *output, int relative, int precision) { const LWLINE *line; uint32_t i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), " "); /* SVG whitespace Separator */ line = mline->geoms[i]; ptr += assvg_line_buf(line, ptr, relative, precision); } return (ptr-output); } static char * assvg_multiline(const LWMLINE *mline, int relative, int precision) { char *output; int size; size = assvg_multiline_size(mline, relative, precision); output = lwalloc(size); assvg_multiline_buf(mline, output, relative, precision); return output; } /* * Multipolygon Geometry */ static size_t assvg_multipolygon_size(const LWMPOLY *mpoly, int relative, int precision) { const LWPOLY *poly; size_t size=0; uint32_t i; for (i=0 ; ingeoms ; i++) { poly = mpoly->geoms[i]; size += assvg_polygon_size(poly, relative, precision); } size += sizeof(" ") * --i; /* SVG whitespace Separator */ return size; } static size_t assvg_multipolygon_buf(const LWMPOLY *mpoly, char *output, int relative, int precision) { const LWPOLY *poly; uint32_t i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), " "); /* SVG whitespace Separator */ poly = mpoly->geoms[i]; ptr += assvg_polygon_buf(poly, ptr, relative, precision); } return (ptr-output); } static char * assvg_multipolygon(const LWMPOLY *mpoly, int relative, int precision) { char *output; int size; size = assvg_multipolygon_size(mpoly, relative, precision); output = lwalloc(size); assvg_multipolygon_buf(mpoly, output, relative, precision); return output; } /** * Collection Geometry */ static size_t assvg_collection_size(const LWCOLLECTION *col, int relative, int precision) { uint32_t i = 0; size_t size=0; const LWGEOM *subgeom; for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += assvg_geom_size(subgeom, relative, precision); } if ( i ) /* We have some geometries, so add space for delimiters. */ size += sizeof(";") * --i; if (size == 0) size++; /* GEOMETRYCOLLECTION EMPTY, space for null terminator */ return size; } static size_t assvg_collection_buf(const LWCOLLECTION *col, char *output, int relative, int precision) { uint32_t i; char *ptr=output; const LWGEOM *subgeom; /* EMPTY GEOMETRYCOLLECTION */ if (col->ngeoms == 0) *ptr = '\0'; for (i=0; ingeoms; i++) { if (i) ptr += snprintf(ptr, strlen(ptr), ";"); subgeom = col->geoms[i]; ptr += assvg_geom_buf(subgeom, ptr, relative, precision); } return (ptr - output); } static char * assvg_collection(const LWCOLLECTION *col, int relative, int precision) { char *output; int size; size = assvg_collection_size(col, relative, precision); output = lwalloc(size); assvg_collection_buf(col, output, relative, precision); return output; } static size_t assvg_geom_buf(const LWGEOM *geom, char *output, int relative, int precision) { int type = geom->type; char *ptr=output; switch (type) { case POINTTYPE: ptr += assvg_point_buf((LWPOINT*)geom, ptr, relative, precision); break; case LINETYPE: ptr += assvg_line_buf((LWLINE*)geom, ptr, relative, precision); break; case POLYGONTYPE: ptr += assvg_polygon_buf((LWPOLY*)geom, ptr, relative, precision); break; case MULTIPOINTTYPE: ptr += assvg_multipoint_buf((LWMPOINT*)geom, ptr, relative, precision); break; case MULTILINETYPE: ptr += assvg_multiline_buf((LWMLINE*)geom, ptr, relative, precision); break; case MULTIPOLYGONTYPE: ptr += assvg_multipolygon_buf((LWMPOLY*)geom, ptr, relative, precision); break; default: lwerror("assvg_geom_buf: '%s' geometry type not supported.", lwtype_name(type)); } return (ptr-output); } static size_t assvg_geom_size(const LWGEOM *geom, int relative, int precision) { int type = geom->type; size_t size = 0; switch (type) { case POINTTYPE: size = assvg_point_size((LWPOINT*)geom, relative, precision); break; case LINETYPE: size = assvg_line_size((LWLINE*)geom, relative, precision); break; case POLYGONTYPE: size = assvg_polygon_size((LWPOLY*)geom, relative, precision); break; case MULTIPOINTTYPE: size = assvg_multipoint_size((LWMPOINT*)geom, relative, precision); break; case MULTILINETYPE: size = assvg_multiline_size((LWMLINE*)geom, relative, precision); break; case MULTIPOLYGONTYPE: size = assvg_multipolygon_size((LWMPOLY*)geom, relative, precision); break; default: lwerror("assvg_geom_size: '%s' geometry type not supported.", lwtype_name(type)); } return size; } static size_t pointArray_svg_rel(POINTARRAY *pa, char *output, int close_ring, int precision) { int i, end; char *ptr; char sx[OUT_DOUBLE_BUFFER_SIZE]; char sy[OUT_DOUBLE_BUFFER_SIZE]; const POINT2D *pt; double f = 1.0; double dx, dy, x, y, accum_x, accum_y; ptr = output; if (precision >= 0) { f = pow(10, precision); } if (close_ring) end = pa->npoints; else end = pa->npoints - 1; /* Starting point */ pt = getPoint2d_cp(pa, 0); x = round(pt->x*f)/f; y = round(pt->y*f)/f; lwprint_double(x, precision, sx, OUT_DOUBLE_BUFFER_SIZE); lwprint_double(-y, precision, sy, OUT_DOUBLE_BUFFER_SIZE); ptr += snprintf(ptr, strlen(ptr), "%s %s l", sx, sy); /* accum */ accum_x = x; accum_y = y; /* All the following ones */ for (i=1 ; i < end ; i++) { // lpt = pt; pt = getPoint2d_cp(pa, i); x = round(pt->x*f)/f; y = round(pt->y*f)/f; dx = x - accum_x; dy = y - accum_y; lwprint_double(dx, precision, sx, OUT_DOUBLE_BUFFER_SIZE); lwprint_double(-dy, precision, sy, OUT_DOUBLE_BUFFER_SIZE); accum_x += dx; accum_y += dy; ptr += snprintf(ptr, strlen(ptr), " %s %s", sx, sy); } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_svg_abs(POINTARRAY *pa, char *output, int close_ring, int precision) { int i, end; char *ptr; char x[OUT_DOUBLE_BUFFER_SIZE]; char y[OUT_DOUBLE_BUFFER_SIZE]; POINT2D pt; ptr = output; if (close_ring) end = pa->npoints; else end = pa->npoints - 1; for (i=0 ; i < end ; i++) { getPoint2d_p(pa, i, &pt); lwprint_double(pt.x, precision, x, OUT_DOUBLE_BUFFER_SIZE); lwprint_double(-pt.y, precision, y, OUT_DOUBLE_BUFFER_SIZE); if (i == 1) ptr += snprintf(ptr, strlen(ptr), " L "); else if (i) ptr += snprintf(ptr, strlen(ptr), " "); ptr += snprintf(ptr, strlen(ptr), "%s %s", x, y); } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_svg_size(POINTARRAY *pa, int precision) { return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(" ")) * 2 * pa->npoints + sizeof(" L "); } lwgeom/src/liblwgeom/lwout_encoded_polyline.c0000644000176200001440000001010013773172540021227 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2014 Kashif Rasul and * Shoaib Burq * **********************************************************************/ #include "stringbuffer.h" #include "liblwgeom_internal.h" static char* lwline_to_encoded_polyline(const LWLINE*, int precision); static char* lwmmpoint_to_encoded_polyline(const LWMPOINT*, int precision); static char* pointarray_to_encoded_polyline(const POINTARRAY*, int precision); /* takes a GEOMETRY and returns an Encoded Polyline representation */ extern char* lwgeom_to_encoded_polyline(const LWGEOM* geom, int precision) { int type = geom->type; switch (type) { case LINETYPE: return lwline_to_encoded_polyline((LWLINE*)geom, precision); case MULTIPOINTTYPE: return lwmmpoint_to_encoded_polyline((LWMPOINT*)geom, precision); default: lwerror("lwgeom_to_encoded_polyline: '%s' geometry type not supported", lwtype_name(type)); return NULL; } } static char* lwline_to_encoded_polyline(const LWLINE* line, int precision) { return pointarray_to_encoded_polyline(line->points, precision); } static char* lwmmpoint_to_encoded_polyline(const LWMPOINT* mpoint, int precision) { LWLINE* line = lwline_from_lwmpoint(mpoint->srid, mpoint); char* encoded_polyline = lwline_to_encoded_polyline(line, precision); lwline_free(line); return encoded_polyline; } static char* pointarray_to_encoded_polyline(const POINTARRAY* pa, int precision) { uint32_t i; const POINT2D* prevPoint; int* delta; char* encoded_polyline = NULL; stringbuffer_t* sb; double scale = pow(10, precision); /* Empty input is empty string */ if (pa->npoints == 0) { encoded_polyline = lwalloc(1 * sizeof(char)); encoded_polyline[0] = 0; return encoded_polyline; } delta = lwalloc(2 * sizeof(int) * pa->npoints); /* Take the double value and multiply it by 1x10^precision, rounding the * result */ prevPoint = getPoint2d_cp(pa, 0); delta[0] = round(prevPoint->y * scale); delta[1] = round(prevPoint->x * scale); /* Points only include the offset from the previous point */ for (i = 1; i < pa->npoints; i++) { const POINT2D* point = getPoint2d_cp(pa, i); delta[2 * i] = round(point->y * scale) - round(prevPoint->y * scale); delta[(2 * i) + 1] = round(point->x * scale) - round(prevPoint->x * scale); prevPoint = point; } /* value to binary: a negative value must be calculated using its two's * complement */ for (i = 0; i < pa->npoints * 2; i++) { /* Multiply by 2 for a signed left shift */ delta[i] *= 2; /* if value is negative, invert this encoding */ if (delta[i] < 0) { delta[i] = ~(delta[i]); } } sb = stringbuffer_create(); for (i = 0; i < pa->npoints * 2; i++) { int numberToEncode = delta[i]; while (numberToEncode >= 0x20) { /* Place the 5-bit chunks into reverse order or each value with 0x20 if another bit chunk follows and add 63*/ int nextValue = (0x20 | (numberToEncode & 0x1f)) + 63; stringbuffer_aprintf(sb, "%c", (char)nextValue); /* Break the binary value out into 5-bit chunks */ numberToEncode >>= 5; } numberToEncode += 63; stringbuffer_aprintf(sb, "%c", (char)numberToEncode); } lwfree(delta); encoded_polyline = stringbuffer_getstringcopy(sb); stringbuffer_destroy(sb); return encoded_polyline; } lwgeom/src/liblwgeom/lwcurvepoly.c0000644000176200001440000001041613773172540017066 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic LWCURVEPOLY manipulation */ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" LWCURVEPOLY * lwcurvepoly_construct_empty(int32_t srid, char hasz, char hasm) { LWCURVEPOLY *ret; ret = lwalloc(sizeof(LWCURVEPOLY)); ret->type = CURVEPOLYTYPE; ret->flags = lwflags(hasz, hasm, 0); ret->srid = srid; ret->nrings = 0; ret->maxrings = 1; /* Allocate room for sub-members, just in case. */ ret->rings = lwalloc(ret->maxrings * sizeof(LWGEOM*)); ret->bbox = NULL; return ret; } LWCURVEPOLY * lwcurvepoly_construct_from_lwpoly(LWPOLY *lwpoly) { LWCURVEPOLY *ret; uint32_t i; ret = lwalloc(sizeof(LWCURVEPOLY)); ret->type = CURVEPOLYTYPE; ret->flags = lwpoly->flags; ret->srid = lwpoly->srid; ret->nrings = lwpoly->nrings; ret->maxrings = lwpoly->nrings; /* Allocate room for sub-members, just in case. */ ret->rings = lwalloc(ret->maxrings * sizeof(LWGEOM*)); ret->bbox = lwpoly->bbox ? gbox_clone(lwpoly->bbox) : NULL; for ( i = 0; i < ret->nrings; i++ ) { ret->rings[i] = lwline_as_lwgeom(lwline_construct(ret->srid, NULL, ptarray_clone_deep(lwpoly->rings[i]))); } return ret; } int lwcurvepoly_add_ring(LWCURVEPOLY *poly, LWGEOM *ring) { uint32_t i; /* Can't do anything with NULLs */ if( ! poly || ! ring ) { LWDEBUG(4,"NULL inputs!!! quitting"); return LW_FAILURE; } /* Check that we're not working with garbage */ if ( poly->rings == NULL && (poly->nrings || poly->maxrings) ) { LWDEBUG(4,"mismatched nrings/maxrings"); lwerror("Curvepolygon is in inconsistent state. Null memory but non-zero collection counts."); return LW_FAILURE; } /* Check that we're adding an allowed ring type */ if ( ! ( ring->type == LINETYPE || ring->type == CIRCSTRINGTYPE || ring->type == COMPOUNDTYPE ) ) { LWDEBUGF(4,"got incorrect ring type: %s",lwtype_name(ring->type)); return LW_FAILURE; } /* In case this is a truly empty, make some initial space */ if ( poly->rings == NULL ) { poly->maxrings = 2; poly->nrings = 0; poly->rings = lwalloc(poly->maxrings * sizeof(LWGEOM*)); } /* Allocate more space if we need it */ if ( poly->nrings == poly->maxrings ) { poly->maxrings *= 2; poly->rings = lwrealloc(poly->rings, sizeof(LWGEOM*) * poly->maxrings); } /* Make sure we don't already have a reference to this geom */ for ( i = 0; i < poly->nrings; i++ ) { if ( poly->rings[i] == ring ) { LWDEBUGF(4, "Found duplicate geometry in collection %p == %p", poly->rings[i], ring); return LW_SUCCESS; } } /* Add the ring and increment the ring count */ poly->rings[poly->nrings] = (LWGEOM*)ring; poly->nrings++; return LW_SUCCESS; } /** * This should be rewritten to make use of the curve itself. */ double lwcurvepoly_area(const LWCURVEPOLY *curvepoly) { double area = 0.0; LWPOLY *poly; if( lwgeom_is_empty((LWGEOM*)curvepoly) ) return 0.0; poly = lwcurvepoly_stroke(curvepoly, 32); area = lwpoly_area(poly); lwpoly_free(poly); return area; } double lwcurvepoly_perimeter(const LWCURVEPOLY *poly) { double result=0.0; uint32_t i; for (i=0; inrings; i++) result += lwgeom_length(poly->rings[i]); return result; } double lwcurvepoly_perimeter_2d(const LWCURVEPOLY *poly) { double result=0.0; uint32_t i; for (i=0; inrings; i++) result += lwgeom_length_2d(poly->rings[i]); return result; } lwgeom/src/liblwgeom/lwout_wkb.c0000644000176200001440000005543413773172540016521 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant); static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant); /* * Look-up table for hex writer */ static char *hexchr = "0123456789ABCDEF"; char* hexbytes_from_bytes(const uint8_t *bytes, size_t size) { char *hex; uint32_t i; if ( ! bytes || ! size ) { lwerror("hexbutes_from_bytes: invalid input"); return NULL; } hex = lwalloc(size * 2 + 1); hex[2*size] = '\0'; for( i = 0; i < size; i++ ) { /* Top four bits to 0-F */ hex[2*i] = hexchr[bytes[i] >> 4]; /* Bottom four bits to 0-F */ hex[2*i+1] = hexchr[bytes[i] & 0x0F]; } return hex; } /* * Optional SRID */ static int lwgeom_wkb_needs_srid(const LWGEOM *geom, uint8_t variant) { /* Sub-components of collections inherit their SRID from the parent. We force that behavior with the WKB_NO_SRID flag */ if ( variant & WKB_NO_SRID ) return LW_FALSE; /* We can only add an SRID if the geometry has one, and the WKB form is extended */ if ( (variant & WKB_EXTENDED) && lwgeom_has_srid(geom) ) return LW_TRUE; /* Everything else doesn't get an SRID */ return LW_FALSE; } /* * GeometryType */ static uint32_t lwgeom_wkb_type(const LWGEOM *geom, uint8_t variant) { uint32_t wkb_type = 0; switch ( geom->type ) { case POINTTYPE: wkb_type = WKB_POINT_TYPE; break; case LINETYPE: wkb_type = WKB_LINESTRING_TYPE; break; case POLYGONTYPE: wkb_type = WKB_POLYGON_TYPE; break; case MULTIPOINTTYPE: wkb_type = WKB_MULTIPOINT_TYPE; break; case MULTILINETYPE: wkb_type = WKB_MULTILINESTRING_TYPE; break; case MULTIPOLYGONTYPE: wkb_type = WKB_MULTIPOLYGON_TYPE; break; case COLLECTIONTYPE: wkb_type = WKB_GEOMETRYCOLLECTION_TYPE; break; case CIRCSTRINGTYPE: wkb_type = WKB_CIRCULARSTRING_TYPE; break; case COMPOUNDTYPE: wkb_type = WKB_COMPOUNDCURVE_TYPE; break; case CURVEPOLYTYPE: wkb_type = WKB_CURVEPOLYGON_TYPE; break; case MULTICURVETYPE: wkb_type = WKB_MULTICURVE_TYPE; break; case MULTISURFACETYPE: wkb_type = WKB_MULTISURFACE_TYPE; break; case POLYHEDRALSURFACETYPE: wkb_type = WKB_POLYHEDRALSURFACE_TYPE; break; case TINTYPE: wkb_type = WKB_TIN_TYPE; break; case TRIANGLETYPE: wkb_type = WKB_TRIANGLE_TYPE; break; default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } if ( variant & WKB_EXTENDED ) { if ( FLAGS_GET_Z(geom->flags) ) wkb_type |= WKBZOFFSET; if ( FLAGS_GET_M(geom->flags) ) wkb_type |= WKBMOFFSET; /* if ( geom->srid != SRID_UNKNOWN && ! (variant & WKB_NO_SRID) ) */ if ( lwgeom_wkb_needs_srid(geom, variant) ) wkb_type |= WKBSRIDFLAG; } else if ( variant & WKB_ISO ) { /* Z types are in the 1000 range */ if ( FLAGS_GET_Z(geom->flags) ) wkb_type += 1000; /* M types are in the 2000 range */ if ( FLAGS_GET_M(geom->flags) ) wkb_type += 2000; /* ZM types are in the 1000 + 2000 = 3000 range, see above */ } return wkb_type; } /* * Endian */ static uint8_t* endian_to_wkb_buf(uint8_t *buf, uint8_t variant) { if ( variant & WKB_HEX ) { buf[0] = '0'; buf[1] = ((variant & WKB_NDR) ? '1' : '0'); return buf + 2; } else { buf[0] = ((variant & WKB_NDR) ? 1 : 0); return buf + 1; } } /* * SwapBytes? */ static inline int wkb_swap_bytes(uint8_t variant) { /* If requested variant matches machine arch, we don't have to swap! */ if (((variant & WKB_NDR) && !IS_BIG_ENDIAN) || ((!(variant & WKB_NDR)) && IS_BIG_ENDIAN)) { return LW_FALSE; } return LW_TRUE; } /* * Integer32 */ static uint8_t * integer_to_wkb_buf(const uint32_t ival, uint8_t *buf, uint8_t variant) { uint8_t *iptr = (uint8_t *)(&ival); int i = 0; if ( sizeof(int) != WKB_INT_SIZE ) { lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE); } LWDEBUGF(4, "Writing value '%u'", ival); if ( variant & WKB_HEX ) { int swap = wkb_swap_bytes(variant); /* Machine/request arch mismatch, so flip byte order */ for ( i = 0; i < WKB_INT_SIZE; i++ ) { int j = (swap ? WKB_INT_SIZE - 1 - i : i); uint8_t b = iptr[j]; /* Top four bits to 0-F */ buf[2*i] = hexchr[b >> 4]; /* Bottom four bits to 0-F */ buf[2*i+1] = hexchr[b & 0x0F]; } return buf + (2 * WKB_INT_SIZE); } else { /* Machine/request arch mismatch, so flip byte order */ if ( wkb_swap_bytes(variant) ) { for ( i = 0; i < WKB_INT_SIZE; i++ ) { buf[i] = iptr[WKB_INT_SIZE - 1 - i]; } } /* If machine arch and requested arch match, don't flip byte order */ else { memcpy(buf, iptr, WKB_INT_SIZE); } return buf + WKB_INT_SIZE; } } static uint8_t* double_nan_to_wkb_buf(uint8_t *buf, uint8_t variant) { #define NAN_SIZE 8 const uint8_t ndr_nan[NAN_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f}; const uint8_t xdr_nan[NAN_SIZE] = {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if ( variant & WKB_HEX ) { for (int i = 0; i < NAN_SIZE; i++) { uint8_t b = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i]; /* Top four bits to 0-F */ buf[2*i] = hexchr[b >> 4]; /* Bottom four bits to 0-F */ buf[2*i + 1] = hexchr[b & 0x0F]; } return buf + (2 * NAN_SIZE); } else { for (int i = 0; i < NAN_SIZE; i++) { buf[i] = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i];; } return buf + NAN_SIZE; } } /* * Float64 */ static uint8_t* double_to_wkb_buf(const double d, uint8_t *buf, uint8_t variant) { uint8_t *dptr = (uint8_t *)(&d); int i = 0; if ( sizeof(double) != WKB_DOUBLE_SIZE ) { lwerror("Machine double size is not %d bytes!", WKB_DOUBLE_SIZE); } if ( variant & WKB_HEX ) { int swap = wkb_swap_bytes(variant); /* Machine/request arch mismatch, so flip byte order */ for ( i = 0; i < WKB_DOUBLE_SIZE; i++ ) { int j = (swap ? WKB_DOUBLE_SIZE - 1 - i : i); uint8_t b = dptr[j]; /* Top four bits to 0-F */ buf[2*i] = hexchr[b >> 4]; /* Bottom four bits to 0-F */ buf[2*i+1] = hexchr[b & 0x0F]; } return buf + (2 * WKB_DOUBLE_SIZE); } else { /* Machine/request arch mismatch, so flip byte order */ if ( wkb_swap_bytes(variant) ) { for ( i = 0; i < WKB_DOUBLE_SIZE; i++ ) { buf[i] = dptr[WKB_DOUBLE_SIZE - 1 - i]; } } /* If machine arch and requested arch match, don't flip byte order */ else { memcpy(buf, dptr, WKB_DOUBLE_SIZE); } return buf + WKB_DOUBLE_SIZE; } } /* * Empty */ static size_t empty_to_wkb_size(const LWGEOM *geom, uint8_t variant) { /* endian byte + type integer */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; /* optional srid integer */ if ( lwgeom_wkb_needs_srid(geom, variant) ) size += WKB_INT_SIZE; /* Represent POINT EMPTY as POINT(NaN NaN) */ if ( geom->type == POINTTYPE ) { const LWPOINT *pt = (LWPOINT*)geom; size += WKB_DOUBLE_SIZE * FLAGS_NDIMS(pt->point->flags); } /* num-elements */ else { size += WKB_INT_SIZE; } return size; } static uint8_t* empty_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant) { uint32_t wkb_type = lwgeom_wkb_type(geom, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(wkb_type, buf, variant); /* Set the SRID if necessary */ if ( lwgeom_wkb_needs_srid(geom, variant) ) buf = integer_to_wkb_buf(geom->srid, buf, variant); /* Represent POINT EMPTY as POINT(NaN NaN) */ if ( geom->type == POINTTYPE ) { const LWPOINT *pt = (LWPOINT*)geom; for (int i = 0; i < FLAGS_NDIMS(pt->point->flags); i++) { buf = double_nan_to_wkb_buf(buf, variant); } } /* Everything else is flagged as empty using num-elements == 0 */ else { /* Set nrings/npoints/ngeoms to zero */ buf = integer_to_wkb_buf(0, buf, variant); } return buf; } /* * POINTARRAY */ static size_t ptarray_to_wkb_size(const POINTARRAY *pa, uint8_t variant) { int dims = 2; size_t size = 0; if ( variant & (WKB_ISO | WKB_EXTENDED) ) dims = FLAGS_NDIMS(pa->flags); /* Include the npoints if it's not a POINT type) */ if ( ! ( variant & WKB_NO_NPOINTS ) ) size += WKB_INT_SIZE; /* size of the double list */ size += pa->npoints * dims * WKB_DOUBLE_SIZE; return size; } static uint8_t* ptarray_to_wkb_buf(const POINTARRAY *pa, uint8_t *buf, uint8_t variant) { uint32_t dims = 2; uint32_t pa_dims = FLAGS_NDIMS(pa->flags); uint32_t i, j; double *dbl_ptr; /* SFSQL is always 2-d. Extended and ISO use all available dimensions */ if ( (variant & WKB_ISO) || (variant & WKB_EXTENDED) ) dims = pa_dims; /* Set the number of points (if it's not a POINT type) */ if ( ! ( variant & WKB_NO_NPOINTS ) ) buf = integer_to_wkb_buf(pa->npoints, buf, variant); /* Bulk copy the coordinates when: dimensionality matches, output format */ /* is not hex, and output endian matches internal endian. */ if ( pa->npoints && (dims == pa_dims) && ! wkb_swap_bytes(variant) && ! (variant & WKB_HEX) ) { size_t size = pa->npoints * dims * WKB_DOUBLE_SIZE; memcpy(buf, getPoint_internal(pa, 0), size); buf += size; } /* Copy coordinates one-by-one otherwise */ else { for ( i = 0; i < pa->npoints; i++ ) { LWDEBUGF(4, "Writing point #%d", i); dbl_ptr = (double*)getPoint_internal(pa, i); for ( j = 0; j < dims; j++ ) { LWDEBUGF(4, "Writing dimension #%d (buf = %p)", j, buf); buf = double_to_wkb_buf(dbl_ptr[j], buf, variant); } } } LWDEBUGF(4, "Done (buf = %p)", buf); return buf; } /* * POINT */ static size_t lwpoint_to_wkb_size(const LWPOINT *pt, uint8_t variant) { /* Endian flag + type number */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) ) return empty_to_wkb_size((LWGEOM*)pt, variant); /* Extended WKB needs space for optional SRID integer */ if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) ) size += WKB_INT_SIZE; /* Points */ size += ptarray_to_wkb_size(pt->point, variant | WKB_NO_NPOINTS); return size; } static uint8_t* lwpoint_to_wkb_buf(const LWPOINT *pt, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) ) return empty_to_wkb_buf((LWGEOM*)pt, buf, variant); /* Set the endian flag */ LWDEBUGF(4, "Entering function, buf = %p", buf); buf = endian_to_wkb_buf(buf, variant); LWDEBUGF(4, "Endian set, buf = %p", buf); /* Set the geometry type */ buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)pt, variant), buf, variant); LWDEBUGF(4, "Type set, buf = %p", buf); /* Set the optional SRID for extended variant */ if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) ) { buf = integer_to_wkb_buf(pt->srid, buf, variant); LWDEBUGF(4, "SRID set, buf = %p", buf); } /* Set the coordinates */ buf = ptarray_to_wkb_buf(pt->point, buf, variant | WKB_NO_NPOINTS); LWDEBUGF(4, "Pointarray set, buf = %p", buf); return buf; } /* * LINESTRING, CIRCULARSTRING */ static size_t lwline_to_wkb_size(const LWLINE *line, uint8_t variant) { /* Endian flag + type number */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) ) return empty_to_wkb_size((LWGEOM*)line, variant); /* Extended WKB needs space for optional SRID integer */ if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) ) size += WKB_INT_SIZE; /* Size of point array */ size += ptarray_to_wkb_size(line->points, variant); return size; } static uint8_t* lwline_to_wkb_buf(const LWLINE *line, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) ) return empty_to_wkb_buf((LWGEOM*)line, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)line, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) ) buf = integer_to_wkb_buf(line->srid, buf, variant); /* Set the coordinates */ buf = ptarray_to_wkb_buf(line->points, buf, variant); return buf; } /* * TRIANGLE */ static size_t lwtriangle_to_wkb_size(const LWTRIANGLE *tri, uint8_t variant) { /* endian flag + type number + number of rings */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) ) return empty_to_wkb_size((LWGEOM*)tri, variant); /* Extended WKB needs space for optional SRID integer */ if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) ) size += WKB_INT_SIZE; /* How big is this point array? */ size += ptarray_to_wkb_size(tri->points, variant); return size; } static uint8_t* lwtriangle_to_wkb_buf(const LWTRIANGLE *tri, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) ) return empty_to_wkb_buf((LWGEOM*)tri, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)tri, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) ) buf = integer_to_wkb_buf(tri->srid, buf, variant); /* Set the number of rings (only one, it's a triangle, buddy) */ buf = integer_to_wkb_buf(1, buf, variant); /* Write that ring */ buf = ptarray_to_wkb_buf(tri->points, buf, variant); return buf; } /* * POLYGON */ static size_t lwpoly_to_wkb_size(const LWPOLY *poly, uint8_t variant) { /* endian flag + type number + number of rings */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; uint32_t i = 0; /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) ) return empty_to_wkb_size((LWGEOM*)poly, variant); /* Extended WKB needs space for optional SRID integer */ if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) ) size += WKB_INT_SIZE; for ( i = 0; i < poly->nrings; i++ ) { /* Size of ring point array */ size += ptarray_to_wkb_size(poly->rings[i], variant); } return size; } static uint8_t* lwpoly_to_wkb_buf(const LWPOLY *poly, uint8_t *buf, uint8_t variant) { uint32_t i; /* Only process empty at this level in the EXTENDED case */ if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) ) return empty_to_wkb_buf((LWGEOM*)poly, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)poly, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) ) buf = integer_to_wkb_buf(poly->srid, buf, variant); /* Set the number of rings */ buf = integer_to_wkb_buf(poly->nrings, buf, variant); for ( i = 0; i < poly->nrings; i++ ) { buf = ptarray_to_wkb_buf(poly->rings[i], buf, variant); } return buf; } /* * MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION * MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON, TIN, * POLYHEDRALSURFACE */ static size_t lwcollection_to_wkb_size(const LWCOLLECTION *col, uint8_t variant) { /* Endian flag + type number + number of subgeoms */ size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; uint32_t i = 0; /* Extended WKB needs space for optional SRID integer */ if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) ) size += WKB_INT_SIZE; for ( i = 0; i < col->ngeoms; i++ ) { /* size of subgeom */ size += lwgeom_to_wkb_size((LWGEOM*)col->geoms[i], variant | WKB_NO_SRID); } return size; } static uint8_t* lwcollection_to_wkb_buf(const LWCOLLECTION *col, uint8_t *buf, uint8_t variant) { uint32_t i; /* Set the endian flag */ buf = endian_to_wkb_buf(buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)col, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) ) buf = integer_to_wkb_buf(col->srid, buf, variant); /* Set the number of sub-geometries */ buf = integer_to_wkb_buf(col->ngeoms, buf, variant); /* Write the sub-geometries. Sub-geometries do not get SRIDs, they inherit from their parents. */ for ( i = 0; i < col->ngeoms; i++ ) { buf = lwgeom_to_wkb_buf(col->geoms[i], buf, variant | WKB_NO_SRID); } return buf; } /* * GEOMETRY */ static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant) { size_t size = 0; if ( geom == NULL ) return 0; /* Short circuit out empty geometries */ if ( (!(variant & WKB_EXTENDED)) && lwgeom_is_empty(geom) ) { return empty_to_wkb_size(geom, variant); } switch ( geom->type ) { case POINTTYPE: size += lwpoint_to_wkb_size((LWPOINT*)geom, variant); break; /* LineString and CircularString both have points elements */ case CIRCSTRINGTYPE: case LINETYPE: size += lwline_to_wkb_size((LWLINE*)geom, variant); break; /* Polygon has nrings and rings elements */ case POLYGONTYPE: size += lwpoly_to_wkb_size((LWPOLY*)geom, variant); break; /* Triangle has one ring of three points */ case TRIANGLETYPE: size += lwtriangle_to_wkb_size((LWTRIANGLE*)geom, variant); break; /* All these Collection types have ngeoms and geoms elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COLLECTIONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: size += lwcollection_to_wkb_size((LWCOLLECTION*)geom, variant); break; /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } return size; } /* TODO handle the TRIANGLE type properly */ static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant) { /* Do not simplify empties when outputting to canonical form */ if (lwgeom_is_empty(geom) && !(variant & WKB_EXTENDED)) return empty_to_wkb_buf(geom, buf, variant); switch ( geom->type ) { case POINTTYPE: return lwpoint_to_wkb_buf((LWPOINT*)geom, buf, variant); /* LineString and CircularString both have 'points' elements */ case CIRCSTRINGTYPE: case LINETYPE: return lwline_to_wkb_buf((LWLINE*)geom, buf, variant); /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: return lwpoly_to_wkb_buf((LWPOLY*)geom, buf, variant); /* Triangle has one ring of three points */ case TRIANGLETYPE: return lwtriangle_to_wkb_buf((LWTRIANGLE*)geom, buf, variant); /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COLLECTIONTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: return lwcollection_to_wkb_buf((LWCOLLECTION*)geom, buf, variant); /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } /* Return value to keep compiler happy. */ return 0; } /** * Convert LWGEOM to a char* in WKB format. Caller is responsible for freeing * the returned array. * * @param variant. Unsigned bitmask value. Accepts one of: WKB_ISO, WKB_EXTENDED, WKB_SFSQL. * Accepts any of: WKB_NDR, WKB_HEX. For example: Variant = ( WKB_ISO | WKB_NDR ) would * return the little-endian ISO form of WKB. For Example: Variant = ( WKB_EXTENDED | WKB_HEX ) * would return the big-endian extended form of WKB, as hex-encoded ASCII (the "canonical form"). * @param size_out If supplied, will return the size of the returned memory segment, * including the null terminator in the case of ASCII. */ uint8_t* lwgeom_to_wkb(const LWGEOM *geom, uint8_t variant, size_t *size_out) { size_t buf_size; uint8_t *buf = NULL; uint8_t *wkb_out = NULL; /* Initialize output size */ if ( size_out ) *size_out = 0; if ( geom == NULL ) { LWDEBUG(4,"Cannot convert NULL into WKB."); lwerror("Cannot convert NULL into WKB."); return NULL; } /* Calculate the required size of the output buffer */ buf_size = lwgeom_to_wkb_size(geom, variant); LWDEBUGF(4, "WKB output size: %d", buf_size); if ( buf_size == 0 ) { LWDEBUG(4,"Error calculating output WKB buffer size."); lwerror("Error calculating output WKB buffer size."); return NULL; } /* Hex string takes twice as much space as binary + a null character */ if ( variant & WKB_HEX ) { buf_size = 2 * buf_size + 1; LWDEBUGF(4, "Hex WKB output size: %d", buf_size); } /* If neither or both variants are specified, choose the native order */ if ( ! (variant & WKB_NDR || variant & WKB_XDR) || (variant & WKB_NDR && variant & WKB_XDR) ) { if (IS_BIG_ENDIAN) variant = variant | WKB_XDR; else variant = variant | WKB_NDR; } /* Allocate the buffer */ buf = lwalloc(buf_size); if ( buf == NULL ) { LWDEBUGF(4,"Unable to allocate %d bytes for WKB output buffer.", buf_size); lwerror("Unable to allocate %d bytes for WKB output buffer.", buf_size); return NULL; } /* Retain a pointer to the front of the buffer for later */ wkb_out = buf; /* Write the WKB into the output buffer */ buf = lwgeom_to_wkb_buf(geom, buf, variant); /* Null the last byte if this is a hex output */ if ( variant & WKB_HEX ) { *buf = '\0'; buf++; } LWDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ if ( buf_size != (size_t) (buf - wkb_out) ) { LWDEBUG(4,"Output WKB is not the same size as the allocated buffer."); lwerror("Output WKB is not the same size as the allocated buffer."); lwfree(wkb_out); return NULL; } /* Report output size */ if ( size_out ) *size_out = buf_size; return wkb_out; } char* lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out) { return (char*)lwgeom_to_wkb(geom, variant | WKB_HEX, size_out); } lwgeom/src/liblwgeom/lwin_wkb.c0000644000176200001440000005214613773172540016315 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "liblwgeom_internal.h" /* NOTE: includes lwgeom_log.h */ #include "lwgeom_log.h" #include #include /** * Used for passing the parse state between the parsing functions. */ typedef struct { const uint8_t *wkb; /* Points to start of WKB */ int32_t srid; /* Current SRID we are handling */ size_t wkb_size; /* Expected size of WKB */ int8_t swap_bytes; /* Do an endian flip? */ int8_t check; /* Simple validity checks on geometries */ int8_t lwtype; /* Current type we are handling */ int8_t has_z; /* Z? */ int8_t has_m; /* M? */ int8_t has_srid; /* SRID? */ int8_t error; /* An error was found (not enough bytes to read) */ const uint8_t *pos; /* Current parse position */ } wkb_parse_state; /** * Internal function declarations. */ LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s); /**********************************************************************/ /* Our static character->number map. Anything > 15 is invalid */ static uint8_t hex2char[256] = { /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* 0-9 */ 0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20, /* A-F */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* a-f */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* not Hex characters (upper 128 characters) */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 }; uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize) { uint8_t *buf = NULL; register uint8_t h1, h2; uint32_t i; if( hexsize % 2 ) lwerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize); buf = lwalloc(hexsize/2); if( ! buf ) lwerror("Unable to allocate memory buffer."); for( i = 0; i < hexsize/2; i++ ) { h1 = hex2char[(int)hexbuf[2*i]]; h2 = hex2char[(int)hexbuf[2*i+1]]; if( h1 > 15 ) lwerror("Invalid hex character (%c) encountered", hexbuf[2*i]); if( h2 > 15 ) lwerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]); /* First character is high bits, second is low bits */ buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F); } return buf; } /**********************************************************************/ /** * Check that we are not about to read off the end of the WKB * array. */ static inline void wkb_parse_state_check(wkb_parse_state *s, size_t next) { if( (s->pos + next) > (s->wkb + s->wkb_size) ) { lwerror("WKB structure does not match expected size!"); s->error = LW_TRUE; } } /** * Take in an unknown kind of wkb type number and ensure it comes out * as an extended WKB type number (with Z/M/SRID flags masked onto the * high bits). */ static void lwtype_from_wkb_state(wkb_parse_state *s, uint32_t wkb_type) { uint32_t wkb_simple_type; LWDEBUG(4, "Entered function"); s->has_z = LW_FALSE; s->has_m = LW_FALSE; s->has_srid = LW_FALSE; /* If any of the higher bits are set, this is probably an extended type. */ if( wkb_type & 0xF0000000 ) { if( wkb_type & WKBZOFFSET ) s->has_z = LW_TRUE; if( wkb_type & WKBMOFFSET ) s->has_m = LW_TRUE; if( wkb_type & WKBSRIDFLAG ) s->has_srid = LW_TRUE; LWDEBUGF(4, "Extended type: has_z=%d has_m=%d has_srid=%d", s->has_z, s->has_m, s->has_srid); } /* Mask off the flags */ wkb_type = wkb_type & 0x0FFFFFFF; /* Strip out just the type number (1-12) from the ISO number (eg 3001-3012) */ wkb_simple_type = wkb_type % 1000; /* Extract the Z/M information from ISO style numbers */ if( wkb_type >= 3000 && wkb_type < 4000 ) { s->has_z = LW_TRUE; s->has_m = LW_TRUE; } else if ( wkb_type >= 2000 && wkb_type < 3000 ) { s->has_m = LW_TRUE; } else if ( wkb_type >= 1000 && wkb_type < 2000 ) { s->has_z = LW_TRUE; } switch (wkb_simple_type) { case WKB_POINT_TYPE: s->lwtype = POINTTYPE; break; case WKB_LINESTRING_TYPE: s->lwtype = LINETYPE; break; case WKB_POLYGON_TYPE: s->lwtype = POLYGONTYPE; break; case WKB_MULTIPOINT_TYPE: s->lwtype = MULTIPOINTTYPE; break; case WKB_MULTILINESTRING_TYPE: s->lwtype = MULTILINETYPE; break; case WKB_MULTIPOLYGON_TYPE: s->lwtype = MULTIPOLYGONTYPE; break; case WKB_GEOMETRYCOLLECTION_TYPE: s->lwtype = COLLECTIONTYPE; break; case WKB_CIRCULARSTRING_TYPE: s->lwtype = CIRCSTRINGTYPE; break; case WKB_COMPOUNDCURVE_TYPE: s->lwtype = COMPOUNDTYPE; break; case WKB_CURVEPOLYGON_TYPE: s->lwtype = CURVEPOLYTYPE; break; case WKB_MULTICURVE_TYPE: s->lwtype = MULTICURVETYPE; break; case WKB_MULTISURFACE_TYPE: s->lwtype = MULTISURFACETYPE; break; case WKB_POLYHEDRALSURFACE_TYPE: s->lwtype = POLYHEDRALSURFACETYPE; break; case WKB_TIN_TYPE: s->lwtype = TINTYPE; break; case WKB_TRIANGLE_TYPE: s->lwtype = TRIANGLETYPE; break; /* PostGIS 1.5 emits 13, 14 for CurvePolygon, MultiCurve */ /* These numbers aren't SQL/MM (numbers currently only */ /* go up to 12. We can handle the old data here (for now??) */ /* converting them into the lwtypes that are intended. */ case WKB_CURVE_TYPE: s->lwtype = CURVEPOLYTYPE; break; case WKB_SURFACE_TYPE: s->lwtype = MULTICURVETYPE; break; default: /* Error! */ lwerror("Unknown WKB type (%d)! Full WKB type number was (%d).", wkb_simple_type, wkb_type); break; } LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype); return; } /** * Byte * Read a byte and advance the parse state forward. */ static char byte_from_wkb_state(wkb_parse_state *s) { char char_value = 0; LWDEBUG(4, "Entered function"); wkb_parse_state_check(s, WKB_BYTE_SIZE); if (s->error) return 0; LWDEBUG(4, "Passed state check"); char_value = s->pos[0]; LWDEBUGF(4, "Read byte value: %x", char_value); s->pos += WKB_BYTE_SIZE; return char_value; } /** * Int32 * Read 4-byte integer and advance the parse state forward. */ static uint32_t integer_from_wkb_state(wkb_parse_state *s) { uint32_t i = 0; wkb_parse_state_check(s, WKB_INT_SIZE); if (s->error) return 0; memcpy(&i, s->pos, WKB_INT_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int j = 0; uint8_t tmp; for( j = 0; j < WKB_INT_SIZE/2; j++ ) { tmp = ((uint8_t*)(&i))[j]; ((uint8_t*)(&i))[j] = ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1]; ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1] = tmp; } } s->pos += WKB_INT_SIZE; return i; } /** * Double * Read an 8-byte double and advance the parse state forward. */ static double double_from_wkb_state(wkb_parse_state *s) { double d = 0; memcpy(&d, s->pos, WKB_DOUBLE_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int i = 0; uint8_t tmp; for( i = 0; i < WKB_DOUBLE_SIZE/2; i++ ) { tmp = ((uint8_t*)(&d))[i]; ((uint8_t*)(&d))[i] = ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1]; ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1] = tmp; } } s->pos += WKB_DOUBLE_SIZE; return d; } /** * POINTARRAY * Read a dynamically sized point array and advance the parse state forward. * First read the number of points, then read the points. */ static POINTARRAY* ptarray_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; uint32_t npoints = 0; static uint32_t maxpoints = UINT_MAX / WKB_DOUBLE_SIZE / 4; /* Calculate the size of this point array. */ npoints = integer_from_wkb_state(s); if (s->error) return NULL; if (npoints > maxpoints) { lwerror("Pointarray length (%d) is too large"); return NULL; } LWDEBUGF(4,"Pointarray has %d points", npoints); if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = npoints * ndims * WKB_DOUBLE_SIZE; /* Empty! */ if( npoints == 0 ) return ptarray_construct(s->has_z, s->has_m, npoints); /* Does the data we want to read exist? */ wkb_parse_state_check(s, pa_size); if (s->error) return NULL; /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately. */ else { uint32_t i = 0; double *dlist; pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints * ndims; i++ ) { dlist[i] = double_from_wkb_state(s); } } return pa; } /** * POINT * Read a WKB point, starting just after the endian byte, * type number and optional srid number. * Advance the parse state forward appropriately. * WKB point has just a set of doubles, with the quantity depending on the * dimension of the point, so this looks like a special case of the above * with only one point. */ static LWPOINT* lwpoint_from_wkb_state(wkb_parse_state *s) { static uint32_t npoints = 1; POINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; const POINT2D *pt; /* Count the dimensions. */ if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = ndims * WKB_DOUBLE_SIZE; /* Does the data we want to read exist? */ wkb_parse_state_check(s, pa_size); if (s->error) return NULL; /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately */ else { uint32_t i = 0; double *dlist; pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < ndims; i++ ) { dlist[i] = double_from_wkb_state(s); } } /* Check for POINT(NaN NaN) ==> POINT EMPTY */ pt = getPoint2d_cp(pa, 0); if ( isnan(pt->x) && isnan(pt->y) ) { ptarray_free(pa); return lwpoint_construct_empty(s->srid, s->has_z, s->has_m); } else { return lwpoint_construct(s->srid, NULL, pa); } } /** * LINESTRING * Read a WKB linestring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (two point minimum). */ static LWLINE* lwline_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (s->error) return NULL; if( pa == NULL || pa->npoints == 0 ) { if (pa) ptarray_free(pa); return lwline_construct_empty(s->srid, s->has_z, s->has_m); } if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { lwerror("%s must have at least two points", lwtype_name(s->lwtype)); return NULL; } return lwline_construct(s->srid, NULL, pa); } /** * CIRCULARSTRING * Read a WKB circularstring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (three point minimum, * odd number of points). */ static LWCIRCSTRING* lwcircstring_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (s->error) return NULL; if( pa == NULL || pa->npoints == 0 ) { if (pa) ptarray_free(pa); return lwcircstring_construct_empty(s->srid, s->has_z, s->has_m); } if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 3 ) { lwerror("%s must have at least three points", lwtype_name(s->lwtype)); return NULL; } if( s->check & LW_PARSER_CHECK_ODD && ! (pa->npoints % 2) ) { lwerror("%s must have an odd number of points", lwtype_name(s->lwtype)); return NULL; } return lwcircstring_construct(s->srid, NULL, pa); } /** * POLYGON * Read a WKB polygon, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * First read the number of rings, then read each ring * (which are structured as point arrays) */ static LWPOLY* lwpoly_from_wkb_state(wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(s); if (s->error) return NULL; uint32_t i = 0; LWPOLY *poly = lwpoly_construct_empty(s->srid, s->has_z, s->has_m); LWDEBUGF(4,"Polygon has %d rings", nrings); /* Empty polygon? */ if( nrings == 0 ) return poly; for( i = 0; i < nrings; i++ ) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (pa == NULL) { lwpoly_free(poly); return NULL; } /* Check for at least four points. */ if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4) { lwpoly_free(poly); LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype)); lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype)); return NULL; } /* Check that first and last points are the same. */ if( s->check & LW_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(pa) ) { lwpoly_free(poly); LWDEBUGF(2, "%s must have closed rings", lwtype_name(s->lwtype)); lwerror("%s must have closed rings", lwtype_name(s->lwtype)); return NULL; } /* Add ring to polygon */ if ( lwpoly_add_ring(poly, pa) == LW_FAILURE ) { lwpoly_free(poly); LWDEBUG(2, "Unable to add ring to polygon"); lwerror("Unable to add ring to polygon"); return NULL; } } return poly; } /** * TRIANGLE * Read a WKB triangle, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * Triangles are encoded like polygons in WKB, but more like linestrings * as lwgeometries. */ static LWTRIANGLE* lwtriangle_from_wkb_state(wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(s); if (s->error) return NULL; LWTRIANGLE *tri = lwtriangle_construct_empty(s->srid, s->has_z, s->has_m); POINTARRAY *pa = NULL; /* Empty triangle? */ if( nrings == 0 ) return tri; /* Should be only one ring. */ if ( nrings != 1 ) lwerror("Triangle has wrong number of rings: %d", nrings); /* There's only one ring, we hope? */ pa = ptarray_from_wkb_state(s); /* If there's no points, return an empty triangle. */ if( pa == NULL ) return tri; /* Check for at least four points. */ if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { LWDEBUGF(2, "%s must have at least four points", lwtype_name(s->lwtype)); lwerror("%s must have at least four points", lwtype_name(s->lwtype)); return NULL; } if( s->check & LW_PARSER_CHECK_ZCLOSURE && ! ptarray_is_closed_z(pa) ) { lwerror("%s must have closed rings", lwtype_name(s->lwtype)); return NULL; } /* Empty TRIANGLE starts w/ empty POINTARRAY, free it first */ if (tri->points) ptarray_free(tri->points); tri->points = pa; return tri; } /** * CURVEPOLYTYPE */ static LWCURVEPOLY* lwcurvepoly_from_wkb_state(wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(s); if (s->error) return NULL; LWCURVEPOLY *cp = lwcurvepoly_construct_empty(s->srid, s->has_z, s->has_m); LWGEOM *geom = NULL; uint32_t i; /* Empty collection? */ if ( ngeoms == 0 ) return cp; for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_wkb_state(s); if ( lwcurvepoly_add_ring(cp, geom) == LW_FAILURE ) { lwgeom_free(geom); lwgeom_free((LWGEOM *)cp); lwerror("Unable to add geometry (%p) to curvepoly (%p)", geom, cp); return NULL; } } return cp; } /** * POLYHEDRALSURFACETYPE */ /** * COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE, COMPOUNDTYPE, * MULTICURVETYPE, MULTISURFACETYPE, * TINTYPE */ static LWCOLLECTION* lwcollection_from_wkb_state(wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(s); if (s->error) return NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, s->srid, s->has_z, s->has_m); LWGEOM *geom = NULL; uint32_t i; LWDEBUGF(4,"Collection has %d components", ngeoms); /* Empty collection? */ if ( ngeoms == 0 ) return col; /* Be strict in polyhedral surface closures */ if ( s->lwtype == POLYHEDRALSURFACETYPE ) s->check |= LW_PARSER_CHECK_ZCLOSURE; for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_wkb_state(s); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwgeom_free(geom); lwgeom_free((LWGEOM *)col); lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * GEOMETRY * Generic handling for WKB geometries. The front of every WKB geometry * (including those embedded in collections) is an endian byte, a type * number and an optional srid number. We handle all those here, then pass * to the appropriate handler for the specific type. */ LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s) { char wkb_little_endian; uint32_t wkb_type; LWDEBUG(4,"Entered function"); /* Fail when handed incorrect starting byte */ wkb_little_endian = byte_from_wkb_state(s); if (s->error) return NULL; if( wkb_little_endian != 1 && wkb_little_endian != 0 ) { LWDEBUG(4,"Leaving due to bad first byte!"); lwerror("Invalid endian flag value encountered."); return NULL; } /* Check the endianness of our input */ s->swap_bytes = LW_FALSE; /* Machine arch is big endian, request is for little */ if (IS_BIG_ENDIAN && wkb_little_endian) s->swap_bytes = LW_TRUE; /* Machine arch is little endian, request is for big */ else if ((!IS_BIG_ENDIAN) && (!wkb_little_endian)) s->swap_bytes = LW_TRUE; /* Read the type number */ wkb_type = integer_from_wkb_state(s); if (s->error) return NULL; LWDEBUGF(4,"Got WKB type number: 0x%X", wkb_type); lwtype_from_wkb_state(s, wkb_type); /* Read the SRID, if necessary */ if( s->has_srid ) { s->srid = clamp_srid(integer_from_wkb_state(s)); if (s->error) return NULL; /* TODO: warn on explicit UNKNOWN srid ? */ LWDEBUGF(4,"Got SRID: %u", s->srid); } /* Do the right thing */ switch( s->lwtype ) { case POINTTYPE: return (LWGEOM*)lwpoint_from_wkb_state(s); break; case LINETYPE: return (LWGEOM*)lwline_from_wkb_state(s); break; case CIRCSTRINGTYPE: return (LWGEOM*)lwcircstring_from_wkb_state(s); break; case POLYGONTYPE: return (LWGEOM*)lwpoly_from_wkb_state(s); break; case TRIANGLETYPE: return (LWGEOM*)lwtriangle_from_wkb_state(s); break; case CURVEPOLYTYPE: return (LWGEOM*)lwcurvepoly_from_wkb_state(s); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM*)lwcollection_from_wkb_state(s); break; /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype)); } /* Return value to keep compiler happy. */ return NULL; } /* TODO add check for SRID consistency */ /** * WKB inputs *must* have a declared size, to prevent malformed WKB from reading * off the end of the memory segment (this stops a malevolent user from declaring * a one-ring polygon to have 10 rings, causing the WKB reader to walk off the * end of the memory). * * Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD, * LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL */ LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check) { wkb_parse_state s; /* Initialize the state appropriately */ s.wkb = wkb; s.wkb_size = wkb_size; s.swap_bytes = LW_FALSE; s.check = check; s.lwtype = 0; s.srid = SRID_UNKNOWN; s.has_z = LW_FALSE; s.has_m = LW_FALSE; s.has_srid = LW_FALSE; s.error = LW_FALSE; s.pos = wkb; if (!wkb || !wkb_size) return NULL; return lwgeom_from_wkb_state(&s); } LWGEOM* lwgeom_from_hexwkb(const char *hexwkb, const char check) { int hexwkb_len; uint8_t *wkb; LWGEOM *lwgeom; if ( ! hexwkb ) { lwerror("lwgeom_from_hexwkb: null input"); return NULL; } hexwkb_len = strlen(hexwkb); wkb = bytes_from_hexbytes(hexwkb, hexwkb_len); lwgeom = lwgeom_from_wkb(wkb, hexwkb_len/2, check); lwfree(wkb); return lwgeom; } lwgeom/src/liblwgeom/lwmval.c0000644000176200001440000001544013773172540015777 0ustar liggesusers /********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2018 Nicklas Avén * **********************************************************************/ #include "liblwgeom_internal.h" static LWGEOM* lwgeom_filter_m_ignore_null(LWGEOM *geom,double min,double max, int returnm); static POINTARRAY* ptarray_filterm(POINTARRAY *pa,double min, double max, int returnm) { LWDEBUGF(2, "Entered %s", __func__); /*Check if M exists * This should already be tested earlier so we don't handle it properly. * If this happens it is because the function is used in another context than filterM and we throw an error*/ if(!FLAGS_GET_M(pa->flags)) lwerror("missing m-value in function %s\n",__func__); /*Dimensions in input geometry*/ int ndims = FLAGS_NDIMS(pa->flags); /*Dimensions in result*/ int res_ndims; if(returnm) res_ndims = ndims; else res_ndims = ndims-1; int pointsize = res_ndims * sizeof(double); double m_val; //M-value will always be the last dimension int m_pos = ndims-1; uint32_t i, counter=0; for(i=0;inpoints;i++) { m_val = *((double*)pa->serialized_pointlist + i*ndims + m_pos); if(m_val >= min && m_val <= max) counter++; } POINTARRAY *pa_res = ptarray_construct(FLAGS_GET_Z(pa->flags),returnm * FLAGS_GET_M(pa->flags), counter); double *res_cursor = (double*) pa_res->serialized_pointlist; for(i=0;inpoints;i++) { m_val = *((double*)pa->serialized_pointlist + i*ndims + m_pos); if(m_val >= min && m_val <= max) { memcpy(res_cursor, (double*) pa->serialized_pointlist + i*ndims, pointsize); res_cursor+=res_ndims; } } return pa_res; } static LWPOINT* lwpoint_filterm(LWPOINT *pt,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s", __func__); POINTARRAY *pa; pa = ptarray_filterm(pt->point, min, max,returnm); if(pa->npoints < 1) { ptarray_free(pa); return NULL; } return lwpoint_construct(pt->srid, NULL, pa); } static LWLINE* lwline_filterm(LWLINE *line,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s", __func__); POINTARRAY *pa; pa = ptarray_filterm(line->points, min, max,returnm); if(pa->npoints < 2 ) { ptarray_free(pa); return NULL; } return lwline_construct(line->srid, NULL, pa); } static LWPOLY* lwpoly_filterm(LWPOLY *poly,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s", __func__); int i, nrings; LWPOLY *poly_res = lwpoly_construct_empty(poly->srid, FLAGS_GET_Z(poly->flags),returnm * FLAGS_GET_M(poly->flags)); nrings = poly->nrings; for( i = 0; i < nrings; i++ ) { /* Ret number of points */ POINTARRAY *pa = ptarray_filterm(poly->rings[i], min, max,returnm); /* Skip empty rings */ if( pa == NULL ) continue; if(pa->npoints>=4) { if (lwpoly_add_ring(poly_res, pa) == LW_FAILURE ) { LWDEBUG(2, "Unable to add ring to polygon"); lwerror("Unable to add ring to polygon"); } } else if (i==0) { ptarray_free(pa); lwpoly_free(poly_res); return NULL; } else ptarray_free(pa); } return poly_res; } static LWCOLLECTION* lwcollection_filterm(const LWCOLLECTION *igeom,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s",__func__); uint32_t i; LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags),returnm * FLAGS_GET_M(igeom->flags)); if( lwcollection_is_empty(igeom) ) return out; for( i = 0; i < igeom->ngeoms; i++ ) { LWGEOM *ngeom = lwgeom_filter_m_ignore_null(igeom->geoms[i],min, max,returnm); if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom); } return out; } static LWGEOM* lwgeom_filter_m_ignore_null(LWGEOM *geom,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s",__func__); LWGEOM *geom_out = NULL; if(!FLAGS_GET_M(geom->flags)) return geom; switch ( geom->type ) { case POINTTYPE: { LWDEBUGF(4,"Type found is Point, %d", geom->type); geom_out = lwpoint_as_lwgeom(lwpoint_filterm((LWPOINT*) geom, min, max,returnm)); break; } case LINETYPE: { LWDEBUGF(4,"Type found is Linestring, %d", geom->type); geom_out = lwline_as_lwgeom(lwline_filterm((LWLINE*) geom, min,max,returnm)); break; } /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: { LWDEBUGF(4,"Type found is Polygon, %d", geom->type); geom_out = lwpoly_as_lwgeom(lwpoly_filterm((LWPOLY*)geom, min, max,returnm)); break; } /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: { LWDEBUGF(4,"Type found is collection, %d", geom->type); geom_out = (LWGEOM*) lwcollection_filterm((LWCOLLECTION*) geom, min, max,returnm); break; } /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } return geom_out; } LWGEOM* lwgeom_filter_m(LWGEOM *geom,double min,double max, int returnm) { LWDEBUGF(2, "Entered %s",__func__); LWGEOM *ngeom = lwgeom_filter_m_ignore_null(geom,min, max,returnm); if(ngeom) return ngeom; else { switch ( geom->type ) { case POINTTYPE: { return (LWGEOM*) lwpoint_construct_empty(geom->srid, FLAGS_GET_Z(geom->flags), returnm * FLAGS_GET_M(geom->flags)); break; } case LINETYPE: { return (LWGEOM*) lwline_construct_empty(geom->srid, FLAGS_GET_Z(geom->flags), returnm * FLAGS_GET_M(geom->flags)); break; } /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: { return (LWGEOM*) lwpoly_construct_empty(geom->srid, FLAGS_GET_Z(geom->flags), returnm * FLAGS_GET_M(geom->flags)); break; } /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: { return (LWGEOM*) lwcollection_construct_empty(geom->type, geom->srid, FLAGS_GET_Z(geom->flags), returnm * FLAGS_GET_M(geom->flags)); break; } /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); } } /*Shouldn't be possible*/ return NULL; } lwgeom/src/liblwgeom/gserialized.h0000644000176200001440000001270113773172540017001 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2019 Paul Ramsey * **********************************************************************/ /* * GSERIALIZED PUBLIC API */ /** * Read the flags from a #GSERIALIZED and return a standard lwflag * integer */ lwflags_t gserialized_get_lwflags(const GSERIALIZED *g); /** * Copy a new bounding box into an existing gserialized. * If necessary a new #GSERIALIZED will be allocated. Test * that input != output before freeing input. */ GSERIALIZED *gserialized_set_gbox(GSERIALIZED *g, GBOX *gbox); /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED *gserialized_drop_gbox(GSERIALIZED *g); /** * Read the box from the #GSERIALIZED or calculate it if necessary. * Return #LWFAILURE if box cannot be calculated (NULL or EMPTY * input). */ int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Read the box from the #GSERIALIZED or return #LWFAILURE if * box is unavailable. */ int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ extern uint32_t gserialized_get_type(const GSERIALIZED *g); /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ extern uint32_t gserialized_max_header_size(void); /** * Returns a hash code for the srid/type/geometry information * in the GSERIALIZED. Ignores metadata like flags and optional * boxes, etc. */ extern int32_t gserialized_hash(const GSERIALIZED *g); /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ extern int32_t gserialized_get_srid(const GSERIALIZED *g); /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ extern void gserialized_set_srid(GSERIALIZED *g, int32_t srid); /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ extern int gserialized_is_empty(const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ extern int gserialized_has_bbox(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has a Z ordinate. */ extern int gserialized_has_z(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an M ordinate. */ extern int gserialized_has_m(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED is a geography. */ extern int gserialized_is_geodetic(const GSERIALIZED *gser); /** * Return the number of dimensions (2, 3, 4) in a geometry */ extern int gserialized_ndims(const GSERIALIZED *gser); /** * Return -1 if g1 is "less than" g2, 1 if g1 is "greater than" * g2 and 0 if g1 and g2 are the "same". Equality is evaluated * with a memcmp and size check. So it is possible that two * identical objects where one lacks a bounding box could be * evaluated as non-equal initially. Greater and less than * are evaluated by calculating a sortable key from the center * point of the object bounds. */ extern int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2); /** * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ GSERIALIZED *gserialized_from_lwgeom(LWGEOM *geom, size_t *size); /** * Return the memory size a GSERIALIZED will occupy for a given LWGEOM. */ size_t gserialized_from_lwgeom_size(const LWGEOM *geom); /** * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates * that are double aligned and suitable for direct reading using getPoint2d_p_ro */ LWGEOM *lwgeom_from_gserialized(const GSERIALIZED *g); /** * Pull a #GBOX from the header of a #GSERIALIZED, if one is available. If * it is not, calculate it from the geometry. If that doesn't work (null * or empty) return LW_FAILURE. */ int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box); /** * Pull a #GBOX from the header of a #GSERIALIZED, if one is available. If * it is not, return LW_FAILURE. */ int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *box); /** * Pull the first point values of a #GSERIALIZED. Only works for POINTTYPE */ int gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point); lwgeom/src/liblwgeom/lwboundingcircle.c0000644000176200001440000001535614332732765020040 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * **********************************************************************/ #include #include "liblwgeom_internal.h" typedef struct { const POINT2D* p1; const POINT2D* p2; const POINT2D* p3; } SUPPORTING_POINTS; static SUPPORTING_POINTS* supporting_points_create(void) { SUPPORTING_POINTS* s = lwalloc(sizeof(SUPPORTING_POINTS)); s->p1 = NULL; s->p2 = NULL; s->p3 = NULL; return s; } static void supporting_points_destroy(SUPPORTING_POINTS* s) { lwfree(s); } static uint32_t num_supporting_points(SUPPORTING_POINTS* support) { uint32_t N = 0; if (support->p1 != NULL) N++; if (support->p2 != NULL) N++; if (support->p3 != NULL) N++; return N; } static int add_supporting_point(SUPPORTING_POINTS* support, const POINT2D* p) { switch(num_supporting_points(support)) { case 0: support->p1 = p; break; case 1: support->p2 = p; break; case 2: support->p3 = p; break; default: return LW_FAILURE; } return LW_SUCCESS; } static int point_inside_circle(const POINT2D* p, const LWBOUNDINGCIRCLE* c) { if (!c) return LW_FALSE; if (distance2d_pt_pt(p, c->center) - c->radius > DBL_EPSILON) return LW_FALSE; return LW_TRUE; } static double det(double m00, double m01, double m10, double m11) { return m00 * m11 - m01 * m10; } static void circumcenter(const POINT2D* a, const POINT2D* b, const POINT2D* c, POINT2D* result) { double cx = c->x; double cy = c->y; double ax = a->x - cx; double ay = a->y - cy; double bx = b->x - cx; double by = b->y - cy; double denom = 2 * det(ax, ay, bx, by); double numx = det(ay, ax * ax + ay * ay, by, bx * bx + by * by); double numy = det(ax, ax * ax + ay * ay, bx, bx * bx + by * by); result->x = cx - numx / denom; result->y = cy + numy / denom; } static void calculate_mbc_1(const SUPPORTING_POINTS* support, LWBOUNDINGCIRCLE* mbc) { mbc->radius = 0; mbc->center->x = support->p1->x; mbc->center->y = support->p1->y; } static void calculate_mbc_2(const SUPPORTING_POINTS* support, LWBOUNDINGCIRCLE* mbc) { double d1, d2; mbc->center->x = 0.5*(support->p1->x + support->p2->x); mbc->center->y = 0.5*(support->p1->y + support->p2->y); d1 = distance2d_pt_pt(mbc->center, support->p1); d2 = distance2d_pt_pt(mbc->center, support->p2); mbc->radius = FP_MAX(d1, d2); } static void calculate_mbc_3(const SUPPORTING_POINTS* support, LWBOUNDINGCIRCLE* mbc) { double d1, d2, d3; circumcenter(support->p1, support->p2, support->p3, mbc->center); d1 = distance2d_pt_pt(mbc->center, support->p1); d2 = distance2d_pt_pt(mbc->center, support->p2); d3 = distance2d_pt_pt(mbc->center, support->p3); mbc->radius = FP_MAX(FP_MAX(d1, d2), d3); } static int calculate_mbc_from_support(SUPPORTING_POINTS* support, LWBOUNDINGCIRCLE* mbc) { switch(num_supporting_points(support)) { case 0: break; case 1: calculate_mbc_1(support, mbc); break; case 2: calculate_mbc_2(support, mbc); break; case 3: calculate_mbc_3(support, mbc); break; default: return LW_FAILURE; } return LW_SUCCESS; } static int calculate_mbc(const POINT2D** points, uint32_t max_n, SUPPORTING_POINTS* support, LWBOUNDINGCIRCLE* mbc) { uint32_t i; if(!calculate_mbc_from_support(support, mbc)) { return LW_FAILURE; } if (num_supporting_points(support) == 3) { /* If we're entering the function with three supporting points already, our circle * is already fully constrained - we couldn't add another supporting point if we * needed to. So, there's no point in proceeding further. Welzl (1991) provides * a much better explanation of why this works. * */ return LW_SUCCESS; } for (i = 0; i < max_n; i++) { if (!point_inside_circle(points[i], mbc)) { /* We've run into a point that isn't inside our circle. To fix this, we'll * go back in time, and re-run the algorithm for each point we've seen so * far, with the constraint that the current point must be on the boundary * of the circle. Then, we'll continue on in this loop with the modified * circle that by definition includes the current point. */ SUPPORTING_POINTS next_support; memcpy(&next_support, support, sizeof(SUPPORTING_POINTS)); add_supporting_point(&next_support, points[i]); if (!calculate_mbc(points, i, &next_support, mbc)) { return LW_FAILURE; } } } return LW_SUCCESS; } static LWBOUNDINGCIRCLE* lwboundingcircle_create(void) { LWBOUNDINGCIRCLE* c = lwalloc(sizeof(LWBOUNDINGCIRCLE)); c->center = lwalloc(sizeof(POINT2D)); c->radius = 0.0; c->center->x = 0.0; c->center->y = 0.0; return c; } void lwboundingcircle_destroy(LWBOUNDINGCIRCLE* c) { lwfree(c->center); lwfree(c); } LWBOUNDINGCIRCLE* lwgeom_calculate_mbc(const LWGEOM* g) { SUPPORTING_POINTS* support; LWBOUNDINGCIRCLE* result; LWPOINTITERATOR* it; uint32_t num_points; POINT2D** points; POINT4D p; uint32_t i; int success; if(g == NULL || lwgeom_is_empty(g)) return LW_FAILURE; num_points = lwgeom_count_vertices(g); it = lwpointiterator_create(g); points = lwalloc(num_points * sizeof(POINT2D*)); for (i = 0; i < num_points; i++) { if(!lwpointiterator_next(it, &p)) { uint32_t j; for (j = 0; j < i; j++) { lwfree(points[j]); } lwpointiterator_destroy(it); lwfree(points); return LW_FAILURE; } points[i] = lwalloc(sizeof(POINT2D)); points[i]->x = p.x; points[i]->y = p.y; } lwpointiterator_destroy(it); support = supporting_points_create(); result = lwboundingcircle_create(); /* Technically, a randomized algorithm would demand that we shuffle the input points * before we call calculate_mbc(). However, we make the (perhaps poor) assumption that * the order we happen to find the points is as good as random, or close enough. * */ success = calculate_mbc((const POINT2D**) points, num_points, support, result); for (i = 0; i < num_points; i++) { lwfree(points[i]); } lwfree(points); supporting_points_destroy(support); if (!success) return NULL; return result; } lwgeom/src/liblwgeom/lwmpoly.c0000644000176200001440000000342313773172540016176 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" void lwmpoly_release(LWMPOLY *lwmpoly) { lwgeom_release(lwmpoly_as_lwgeom(lwmpoly)); } LWMPOLY * lwmpoly_construct_empty(int32_t srid, char hasz, char hasm) { LWMPOLY *ret = (LWMPOLY*)lwcollection_construct_empty(MULTIPOLYGONTYPE, srid, hasz, hasm); return ret; } LWMPOLY* lwmpoly_add_lwpoly(LWMPOLY *mobj, const LWPOLY *obj) { return (LWMPOLY*)lwcollection_add_lwgeom((LWCOLLECTION*)mobj, (LWGEOM*)obj); } void lwmpoly_free(LWMPOLY *mpoly) { uint32_t i; if ( ! mpoly ) return; if ( mpoly->bbox ) lwfree(mpoly->bbox); for ( i = 0; i < mpoly->ngeoms; i++ ) if ( mpoly->geoms && mpoly->geoms[i] ) lwpoly_free(mpoly->geoms[i]); if ( mpoly->geoms ) lwfree(mpoly->geoms); lwfree(mpoly); } lwgeom/src/liblwgeom/lwmcurve.c0000644000176200001440000000206113773172540016334 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "liblwgeom_internal.h" lwgeom/src/liblwgeom/gserialized1.c0000644000176200001440000011762413773172540017067 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * Copyright 2017 Darafei Praliaskouski * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include "lwgeodetic.h" #include "gserialized1.h" /*********************************************************************** * GSERIALIZED metadata utility functions. */ static int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox); lwflags_t gserialized1_get_lwflags(const GSERIALIZED *g) { lwflags_t lwflags = 0; uint8_t gflags = g->gflags; FLAGS_SET_Z(lwflags, G1FLAGS_GET_Z(gflags)); FLAGS_SET_M(lwflags, G1FLAGS_GET_M(gflags)); FLAGS_SET_BBOX(lwflags, G1FLAGS_GET_BBOX(gflags)); FLAGS_SET_GEODETIC(lwflags, G1FLAGS_GET_GEODETIC(gflags)); FLAGS_SET_SOLID(lwflags, G1FLAGS_GET_SOLID(gflags)); return lwflags; } uint8_t lwflags_get_g1flags(lwflags_t lwflags) { uint8_t gflags = 0; G1FLAGS_SET_Z(gflags, FLAGS_GET_Z(lwflags)); G1FLAGS_SET_M(gflags, FLAGS_GET_M(lwflags)); G1FLAGS_SET_BBOX(gflags, FLAGS_GET_BBOX(lwflags)); G1FLAGS_SET_GEODETIC(gflags, FLAGS_GET_GEODETIC(lwflags)); G1FLAGS_SET_SOLID(gflags, FLAGS_GET_SOLID(lwflags)); return gflags; } static size_t gserialized1_box_size(const GSERIALIZED *g) { if (G1FLAGS_GET_GEODETIC(g->gflags)) return 6 * sizeof(float); else return 2 * G1FLAGS_NDIMS(g->gflags) * sizeof(float); } /* handle missaligned uint32_t data */ static inline uint32_t gserialized1_get_uint32_t(const uint8_t *loc) { return *((uint32_t*)loc); } uint8_t g1flags(int has_z, int has_m, int is_geodetic) { uint8_t gflags = 0; if (has_z) G1FLAGS_SET_Z(gflags, 1); if (has_m) G1FLAGS_SET_M(gflags, 1); if (is_geodetic) G1FLAGS_SET_GEODETIC(gflags, 1); return gflags; } int gserialized1_has_bbox(const GSERIALIZED *gser) { return G1FLAGS_GET_BBOX(gser->gflags); } int gserialized1_has_z(const GSERIALIZED *gser) { return G1FLAGS_GET_Z(gser->gflags); } int gserialized1_has_m(const GSERIALIZED *gser) { return G1FLAGS_GET_M(gser->gflags); } int gserialized1_ndims(const GSERIALIZED *gser) { return G1FLAGS_NDIMS(gser->gflags); } int gserialized1_is_geodetic(const GSERIALIZED *gser) { return G1FLAGS_GET_GEODETIC(gser->gflags); } uint32_t gserialized1_max_header_size(void) { static const intptr_t size_of_gserialized_up_to_data = (intptr_t) & ((GSERIALIZED *)NULL)->data; /* GSERIALIZED size + max bbox according gbox_serialized_size (XYZM*2) + extended flags + type */ return size_of_gserialized_up_to_data + 8 * sizeof(float) + sizeof(uint32_t); } static uint32_t gserialized1_header_size(const GSERIALIZED *gser) { uint32_t sz = 8; /* varsize (4) + srid(3) + flags (1) */ if (gserialized1_has_bbox(gser)) sz += gserialized1_box_size(gser); return sz; } uint32_t gserialized1_get_type(const GSERIALIZED *g) { uint32_t *ptr; ptr = (uint32_t*)(g->data); if ( G1FLAGS_GET_BBOX(g->gflags) ) { ptr += (gserialized1_box_size(g) / sizeof(uint32_t)); } return *ptr; } int32_t gserialized1_get_srid(const GSERIALIZED *s) { int32_t srid = 0; srid = srid | (s->srid[0] << 16); srid = srid | (s->srid[1] << 8); srid = srid | s->srid[2]; /* Only the first 21 bits are set. Slide up and back to pull the negative bits down, if we need them. */ srid = (srid<<11)>>11; /* 0 is our internal unknown value. We'll map back and forth here for now */ if ( srid == 0 ) return SRID_UNKNOWN; else return srid; } void gserialized1_set_srid(GSERIALIZED *s, int32_t srid) { LWDEBUGF(3, "%s called with srid = %d", __func__, srid); srid = clamp_srid(srid); /* 0 is our internal unknown value. * We'll map back and forth here for now */ if ( srid == SRID_UNKNOWN ) srid = 0; s->srid[0] = (srid & 0x001F0000) >> 16; s->srid[1] = (srid & 0x0000FF00) >> 8; s->srid[2] = (srid & 0x000000FF); } static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty); static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty) { int i; int32_t type, num; memcpy(&type, p, 4); memcpy(&num, p+4, 4); if ( lwtype_is_collection(type) ) { size_t lz = 8; for ( i = 0; i < num; i++ ) { lz += gserialized1_is_empty_recurse(p+lz, isempty); if ( ! *isempty ) return lz; } *isempty = LW_TRUE; return lz; } else { *isempty = (num == 0 ? LW_TRUE : LW_FALSE); return 8; } } int gserialized1_is_empty(const GSERIALIZED *g) { uint8_t *p = (uint8_t*)g; int isempty = 0; assert(g); p += 8; /* Skip varhdr and srid/flags */ if(gserialized1_has_bbox(g)) p += gserialized1_box_size(g); /* Skip the box */ gserialized1_is_empty_recurse(p, &isempty); return isempty; } /* Prototype for lookup3.c */ /* key = the key to hash */ /* length = length of the key */ /* pc = IN: primary initval, OUT: primary hash */ /* pb = IN: secondary initval, OUT: secondary hash */ void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); int32_t gserialized1_hash(const GSERIALIZED *g1) { int32_t hval; int32_t pb = 0, pc = 0; /* Point to just the type/coordinate part of buffer */ size_t hsz1 = gserialized1_header_size(g1); uint8_t *b1 = (uint8_t*)g1 + hsz1; /* Calculate size of type/coordinate buffer */ size_t sz1 = SIZE_GET(g1->size); size_t bsz1 = sz1 - hsz1; /* Calculate size of srid/type/coordinate buffer */ int32_t srid = gserialized1_get_srid(g1); size_t bsz2 = bsz1 + sizeof(int); uint8_t *b2 = lwalloc(bsz2); /* Copy srid into front of combined buffer */ memcpy(b2, &srid, sizeof(int)); /* Copy type/coordinates into rest of combined buffer */ memcpy(b2+sizeof(int), b1, bsz1); /* Hash combined buffer */ hashlittle2(b2, bsz2, (uint32_t *)&pb, (uint32_t *)&pc); lwfree(b2); hval = pb ^ pc; return hval; } int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox) { /* Null input! */ if ( ! ( g && gbox ) ) return LW_FAILURE; /* Initialize the flags on the box */ gbox->flags = gserialized1_get_lwflags(g); /* Has pre-calculated box */ if ( G1FLAGS_GET_BBOX(g->gflags) ) { int i = 0; float *fbox = (float*)(g->data); gbox->xmin = fbox[i++]; gbox->xmax = fbox[i++]; gbox->ymin = fbox[i++]; gbox->ymax = fbox[i++]; /* Geodetic? Read next dimension (geocentric Z) and return */ if ( G1FLAGS_GET_GEODETIC(g->gflags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; return LW_SUCCESS; } /* Cartesian? Read extra dimensions (if there) and return */ if ( G1FLAGS_GET_Z(g->gflags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; } if ( G1FLAGS_GET_M(g->gflags) ) { gbox->mmin = fbox[i++]; gbox->mmax = fbox[i++]; } return LW_SUCCESS; } return LW_FAILURE; } /* * Populate a bounding box *without* allocating an LWGEOM. Useful * for some performance purposes. */ int gserialized1_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) { uint32_t type = gserialized1_get_type(g); /* Peeking doesn't help if you already have a box or are geodetic */ if ( G1FLAGS_GET_GEODETIC(g->gflags) || G1FLAGS_GET_BBOX(g->gflags) ) { return LW_FAILURE; } /* Boxes of points are easy peasy */ if ( type == POINTTYPE ) { int i = 1; /* Start past */ double *dptr = (double*)(g->data); /* Read the empty flag */ int32_t *iptr = (int32_t *)(g->data); int isempty = (iptr[1] == 0); /* EMPTY point has no box */ if ( isempty ) return LW_FAILURE; gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; gbox->flags = gserialized1_get_lwflags(g); if ( G1FLAGS_GET_Z(g->gflags) ) { gbox->zmin = gbox->zmax = dptr[i++]; } if ( G1FLAGS_GET_M(g->gflags) ) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(gbox); return LW_SUCCESS; } /* We can calculate the box of a two-point cartesian line trivially */ else if ( type == LINETYPE ) { int ndims = G1FLAGS_NDIMS(g->gflags); int i = 0; /* Start at */ double *dptr = (double*)(g->data); int32_t *iptr = (int32_t *)(g->data); int npoints = iptr[1]; /* Read the npoints */ /* This only works with 2-point lines */ if ( npoints != 2 ) return LW_FAILURE; /* Advance to X */ /* Past */ i++; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); gbox->flags = gserialized1_get_lwflags(g); if ( G1FLAGS_GET_Z(g->gflags) ) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if ( G1FLAGS_GET_M(g->gflags) ) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(gbox); return LW_SUCCESS; } /* We can also do single-entry multi-points */ else if ( type == MULTIPOINTTYPE ) { int i = 0; /* Start at */ double *dptr = (double*)(g->data); int32_t *iptr = (int32_t *)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; /* This only works with single-entry multipoints */ if ( ngeoms != 1 ) return LW_FAILURE; /* Npoints is at */ npoints = iptr[3]; /* The check below is necessary because we can have a MULTIPOINT * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */ if ( npoints != 1 ) return LW_FAILURE; /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; /* Read the doubles from the one point */ gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; gbox->flags = gserialized1_get_lwflags(g); if ( G1FLAGS_GET_Z(g->gflags) ) { gbox->zmin = gbox->zmax = dptr[i++]; } if ( G1FLAGS_GET_M(g->gflags) ) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(gbox); return LW_SUCCESS; } /* And we can do single-entry multi-lines with two vertices (!!!) */ else if ( type == MULTILINETYPE ) { int ndims = G1FLAGS_NDIMS(g->gflags); int i = 0; /* Start at */ double *dptr = (double*)(g->data); int32_t *iptr = (int32_t *)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; /* This only works with 1-line multilines */ if ( ngeoms != 1 ) return LW_FAILURE; /* Npoints is at */ npoints = iptr[3]; if ( npoints != 2 ) return LW_FAILURE; /* Advance to X */ /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); gbox->flags = gserialized1_get_lwflags(g); if ( G1FLAGS_GET_Z(g->gflags) ) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if ( G1FLAGS_GET_M(g->gflags) ) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(gbox); return LW_SUCCESS; } return LW_FAILURE; } static inline void gserialized1_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point) { uint8_t dim = 0; out_point->x = dptr[dim++]; out_point->y = dptr[dim++]; if (G1FLAGS_GET_Z(flags)) { out_point->z = dptr[dim++]; } if (G1FLAGS_GET_M(flags)) { out_point->m = dptr[dim]; } } int gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point) { uint8_t *geometry_start = ((uint8_t *)g->data); if (gserialized1_has_bbox(g)) { geometry_start += gserialized1_box_size(g); } uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0; if (isEmpty) { return LW_FAILURE; } uint32_t type = (((uint32_t *)geometry_start)[0]); /* Setup double_array_start depending on the geometry type */ double *double_array_start = NULL; switch (type) { case (POINTTYPE): /* For points we only need to jump over the type and npoints 32b ints */ double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t)); break; default: lwerror("%s is currently not implemented for type %d", __func__, type); return LW_FAILURE; } gserialized1_copy_point(double_array_start, g->gflags, out_point); return LW_SUCCESS; } /** * Read the bounding box off a serialization and calculate one if * it is not already there. */ int gserialized1_get_gbox_p(const GSERIALIZED *g, GBOX *box) { /* Try to just read the serialized box. */ if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an lwgeom */ else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } /* Damn! Nothing for it but to create an lwgeom... */ /* See http://trac.osgeo.org/postgis/ticket/1023 */ else { LWGEOM *lwgeom = lwgeom_from_gserialized(g); int ret = lwgeom_calculate_gbox(lwgeom, box); gbox_float_round(box); lwgeom_free(lwgeom); return ret; } } /** * Read the bounding box off a serialization and fail if * it is not already there. */ int gserialized1_fast_gbox_p(const GSERIALIZED *g, GBOX *box) { /* Try to just read the serialized box. */ if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an lwgeom */ else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } else { return LW_FAILURE; } } /*********************************************************************** * Calculate the GSERIALIZED size for an LWGEOM. */ /* Private functions */ static size_t gserialized1_from_any_size(const LWGEOM *geom); /* Local prototype */ static size_t gserialized1_from_lwpoint_size(const LWPOINT *point) { size_t size = 4; /* Type number. */ assert(point); size += 4; /* Number of points (one or zero (empty)). */ size += point->point->npoints * FLAGS_NDIMS(point->flags) * sizeof(double); LWDEBUGF(3, "point size = %d", size); return size; } static size_t gserialized1_from_lwline_size(const LWLINE *line) { size_t size = 4; /* Type number. */ assert(line); size += 4; /* Number of points (zero => empty). */ size += line->points->npoints * FLAGS_NDIMS(line->flags) * sizeof(double); LWDEBUGF(3, "linestring size = %d", size); return size; } static size_t gserialized1_from_lwtriangle_size(const LWTRIANGLE *triangle) { size_t size = 4; /* Type number. */ assert(triangle); size += 4; /* Number of points (zero => empty). */ size += triangle->points->npoints * FLAGS_NDIMS(triangle->flags) * sizeof(double); LWDEBUGF(3, "triangle size = %d", size); return size; } static size_t gserialized1_from_lwpoly_size(const LWPOLY *poly) { size_t size = 4; /* Type number. */ uint32_t i = 0; assert(poly); size += 4; /* Number of rings (zero => empty). */ if ( poly->nrings % 2 ) size += 4; /* Padding to double alignment. */ for ( i = 0; i < poly->nrings; i++ ) { size += 4; /* Number of points in ring. */ size += poly->rings[i]->npoints * FLAGS_NDIMS(poly->flags) * sizeof(double); } LWDEBUGF(3, "polygon size = %d", size); return size; } static size_t gserialized1_from_lwcircstring_size(const LWCIRCSTRING *curve) { size_t size = 4; /* Type number. */ assert(curve); size += 4; /* Number of points (zero => empty). */ size += curve->points->npoints * FLAGS_NDIMS(curve->flags) * sizeof(double); LWDEBUGF(3, "circstring size = %d", size); return size; } static size_t gserialized1_from_lwcollection_size(const LWCOLLECTION *col) { size_t size = 4; /* Type number. */ uint32_t i = 0; assert(col); size += 4; /* Number of sub-geometries (zero => empty). */ for ( i = 0; i < col->ngeoms; i++ ) { size_t subsize = gserialized1_from_any_size(col->geoms[i]); size += subsize; LWDEBUGF(3, "lwcollection subgeom(%d) size = %d", i, subsize); } LWDEBUGF(3, "lwcollection size = %d", size); return size; } static size_t gserialized1_from_any_size(const LWGEOM *geom) { LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type)); switch (geom->type) { case POINTTYPE: return gserialized1_from_lwpoint_size((LWPOINT *)geom); case LINETYPE: return gserialized1_from_lwline_size((LWLINE *)geom); case POLYGONTYPE: return gserialized1_from_lwpoly_size((LWPOLY *)geom); case TRIANGLETYPE: return gserialized1_from_lwtriangle_size((LWTRIANGLE *)geom); case CIRCSTRINGTYPE: return gserialized1_from_lwcircstring_size((LWCIRCSTRING *)geom); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return gserialized1_from_lwcollection_size((LWCOLLECTION *)geom); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } } /* Public function */ size_t gserialized1_from_lwgeom_size(const LWGEOM *geom) { size_t size = 8; /* Header overhead. */ assert(geom); if (geom->bbox) size += gbox_serialized_size(geom->flags); size += gserialized1_from_any_size(geom); LWDEBUGF(3, "%s size = %d", __func__, size); return size; } /*********************************************************************** * Serialize an LWGEOM into GSERIALIZED. */ /* Private functions */ static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf); static size_t gserialized1_from_lwpoint(const LWPOINT *point, uint8_t *buf) { uint8_t *loc; int ptsize = ptarray_point_size(point->point); int type = POINTTYPE; assert(point); assert(buf); if ( FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags) ) lwerror("Dimensions mismatch in lwpoint"); LWDEBUGF(2, "%s (%p, %p) called", __func__, point, buf); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of points (0 => empty). */ memcpy(loc, &(point->point->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if ( point->point->npoints > 0 ) { memcpy(loc, getPoint_internal(point->point, 0), ptsize); loc += ptsize; } return (size_t)(loc - buf); } static size_t gserialized1_from_lwline(const LWLINE *line, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = LINETYPE; assert(line); assert(buf); LWDEBUGF(2, "%s (%p, %p) called", __func__, line, buf); if ( FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags) ) lwerror("Dimensions mismatch in lwline"); ptsize = ptarray_point_size(line->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(line->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); LWDEBUGF(3, "%s added npoints (%d)", __func__, line->points->npoints); /* Copy in the ordinates. */ if ( line->points->npoints > 0 ) { size = line->points->npoints * ptsize; memcpy(loc, getPoint_internal(line->points, 0), size); loc += size; } LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * line->points->npoints); return (size_t)(loc - buf); } static size_t gserialized1_from_lwpoly(const LWPOLY *poly, uint8_t *buf) { uint32_t i; uint8_t *loc; int ptsize; int type = POLYGONTYPE; assert(poly); assert(buf); LWDEBUGF(2, "%s called", __func__); ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the nrings. */ memcpy(loc, &(poly->nrings), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints per ring. */ for ( i = 0; i < poly->nrings; i++ ) { memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Add in padding if necessary to remain double aligned. */ if ( poly->nrings % 2 ) { memset(loc, 0, sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Copy in the ordinates. */ for ( i = 0; i < poly->nrings; i++ ) { POINTARRAY *pa = poly->rings[i]; size_t pasize; if ( FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags) ) lwerror("Dimensions mismatch in lwpoly"); pasize = pa->npoints * ptsize; if ( pa->npoints > 0 ) memcpy(loc, getPoint_internal(pa, 0), pasize); loc += pasize; } return (size_t)(loc - buf); } static size_t gserialized1_from_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = TRIANGLETYPE; assert(triangle); assert(buf); LWDEBUGF(2, "%s (%p, %p) called", __func__, triangle, buf); if ( FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags) ) lwerror("Dimensions mismatch in lwtriangle"); ptsize = ptarray_point_size(triangle->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); LWDEBUGF(3, "%s added npoints (%d)", __func__, triangle->points->npoints); /* Copy in the ordinates. */ if ( triangle->points->npoints > 0 ) { size = triangle->points->npoints * ptsize; memcpy(loc, getPoint_internal(triangle->points, 0), size); loc += size; } LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * triangle->points->npoints); return (size_t)(loc - buf); } static size_t gserialized1_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = CIRCSTRINGTYPE; assert(curve); assert(buf); if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags)) lwerror("Dimensions mismatch in lwcircstring"); ptsize = ptarray_point_size(curve->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &curve->points->npoints, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if ( curve->points->npoints > 0 ) { size = curve->points->npoints * ptsize; memcpy(loc, getPoint_internal(curve->points, 0), size); loc += size; } return (size_t)(loc - buf); } static size_t gserialized1_from_lwcollection(const LWCOLLECTION *coll, uint8_t *buf) { size_t subsize = 0; uint8_t *loc; uint32_t i; int type; assert(coll); assert(buf); type = coll->type; loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of subgeoms. */ memcpy(loc, &coll->ngeoms, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Serialize subgeoms. */ for ( i=0; ingeoms; i++ ) { if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags)) lwerror("Dimensions mismatch in lwcollection"); subsize = gserialized1_from_lwgeom_any(coll->geoms[i], loc); loc += subsize; } return (size_t)(loc - buf); } static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf) { assert(geom); assert(buf); LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d", geom->type, lwtype_name(geom->type), FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags)); LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf); switch (geom->type) { case POINTTYPE: return gserialized1_from_lwpoint((LWPOINT *)geom, buf); case LINETYPE: return gserialized1_from_lwline((LWLINE *)geom, buf); case POLYGONTYPE: return gserialized1_from_lwpoly((LWPOLY *)geom, buf); case TRIANGLETYPE: return gserialized1_from_lwtriangle((LWTRIANGLE *)geom, buf); case CIRCSTRINGTYPE: return gserialized1_from_lwcircstring((LWCIRCSTRING *)geom, buf); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return gserialized1_from_lwcollection((LWCOLLECTION *)geom, buf); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } return 0; } static size_t gserialized1_from_gbox(const GBOX *gbox, uint8_t *buf) { uint8_t *loc = buf; float f; size_t return_size; assert(buf); f = next_float_down(gbox->xmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->xmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_down(gbox->ymin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->ymax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); if ( FLAGS_GET_GEODETIC(gbox->flags) ) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } if ( FLAGS_GET_Z(gbox->flags) ) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } if ( FLAGS_GET_M(gbox->flags) ) { f = next_float_down(gbox->mmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->mmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } /* Public function */ GSERIALIZED* gserialized1_from_lwgeom(LWGEOM *geom, size_t *size) { size_t expected_size = 0; size_t return_size = 0; uint8_t *serialized = NULL; uint8_t *ptr = NULL; GSERIALIZED *g = NULL; assert(geom); /* ** See if we need a bounding box, add one if we don't have one. */ if ( (! geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom)) ) { lwgeom_add_bbox(geom); } /* ** Harmonize the flags to the state of the lwgeom */ if ( geom->bbox ) FLAGS_SET_BBOX(geom->flags, 1); else FLAGS_SET_BBOX(geom->flags, 0); /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */ expected_size = gserialized1_from_lwgeom_size(geom); serialized = lwalloc(expected_size); ptr = serialized; /* Move past size, srid and flags. */ ptr += 8; /* Write in the serialized form of the gbox, if necessary. */ if ( geom->bbox ) ptr += gserialized1_from_gbox(geom->bbox, ptr); /* Write in the serialized form of the geometry. */ ptr += gserialized1_from_lwgeom_any(geom, ptr); /* Calculate size as returned by data processing functions. */ return_size = ptr - serialized; if ( expected_size != return_size ) /* Uh oh! */ { lwerror("Return size (%d) not equal to expected size (%d)!", return_size, expected_size); return NULL; } if ( size ) /* Return the output size to the caller if necessary. */ *size = return_size; g = (GSERIALIZED*)serialized; /* ** We are aping PgSQL code here, PostGIS code should use ** VARSIZE to set this for real. */ g->size = return_size << 2; /* Set the SRID! */ gserialized1_set_srid(g, geom->srid); g->gflags = lwflags_get_g1flags(geom->flags); return g; } /*********************************************************************** * De-serialize GSERIALIZED into an LWGEOM. */ static LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size); static LWPOINT* lwpoint_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWPOINT *point; uint32_t npoints = 0; assert(data_ptr); point = (LWPOINT*)lwalloc(sizeof(LWPOINT)); point->srid = SRID_UNKNOWN; /* Default */ point->bbox = NULL; point->type = POINTTYPE; point->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) point->point = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 1, data_ptr); else point->point = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty point */ data_ptr += npoints * FLAGS_NDIMS(lwflags) * sizeof(double); if ( size ) *size = data_ptr - start_ptr; return point; } static LWLINE* lwline_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWLINE *line; uint32_t npoints = 0; assert(data_ptr); line = (LWLINE*)lwalloc(sizeof(LWLINE)); line->srid = SRID_UNKNOWN; /* Default */ line->bbox = NULL; line->type = LINETYPE; line->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) line->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else line->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty linestring */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if ( size ) *size = data_ptr - start_ptr; return line; } static LWPOLY* lwpoly_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWPOLY *poly; uint8_t *ordinate_ptr; uint32_t nrings = 0; uint32_t i = 0; assert(data_ptr); poly = (LWPOLY*)lwalloc(sizeof(LWPOLY)); poly->srid = SRID_UNKNOWN; /* Default */ poly->bbox = NULL; poly->type = POLYGONTYPE; poly->flags = lwflags; data_ptr += 4; /* Skip past the polygontype. */ nrings = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */ poly->nrings = nrings; LWDEBUGF(4, "nrings = %d", nrings); data_ptr += 4; /* Skip past the nrings. */ ordinate_ptr = data_ptr; /* Start the ordinate pointer. */ if ( nrings > 0) { poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings ); poly->maxrings = nrings; ordinate_ptr += nrings * 4; /* Move past all the npoints values. */ if ( nrings % 2 ) /* If there is padding, move past that too. */ ordinate_ptr += 4; } else /* Empty polygon */ { poly->rings = NULL; poly->maxrings = 0; } for ( i = 0; i < nrings; i++ ) { uint32_t npoints = 0; /* Read in the number of points. */ npoints = gserialized1_get_uint32_t(data_ptr); data_ptr += 4; /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */ poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, ordinate_ptr); ordinate_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints; } if ( size ) *size = ordinate_ptr - start_ptr; return poly; } static LWTRIANGLE* lwtriangle_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWTRIANGLE *triangle; uint32_t npoints = 0; assert(data_ptr); triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE)); triangle->srid = SRID_UNKNOWN; /* Default */ triangle->bbox = NULL; triangle->type = TRIANGLETYPE; triangle->flags = lwflags; data_ptr += 4; /* Skip past the type. */ npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else triangle->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty triangle */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if ( size ) *size = data_ptr - start_ptr; return triangle; } static LWCIRCSTRING* lwcircstring_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint8_t *start_ptr = data_ptr; LWCIRCSTRING *circstring; uint32_t npoints = 0; assert(data_ptr); circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING)); circstring->srid = SRID_UNKNOWN; /* Default */ circstring->bbox = NULL; circstring->type = CIRCSTRINGTYPE; circstring->flags = lwflags; data_ptr += 4; /* Skip past the circstringtype. */ npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr); else circstring->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty circularstring */ data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double); if ( size ) *size = data_ptr - start_ptr; return circstring; } static LWCOLLECTION* lwcollection_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size) { uint32_t type; uint8_t *start_ptr = data_ptr; LWCOLLECTION *collection; uint32_t ngeoms = 0; uint32_t i = 0; assert(data_ptr); type = gserialized1_get_uint32_t(data_ptr); data_ptr += 4; /* Skip past the type. */ collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION)); collection->srid = SRID_UNKNOWN; /* Default */ collection->bbox = NULL; collection->type = type; collection->flags = lwflags; ngeoms = gserialized1_get_uint32_t(data_ptr); collection->ngeoms = ngeoms; /* Zero => empty geometry */ data_ptr += 4; /* Skip past the ngeoms. */ if ( ngeoms > 0 ) { collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); collection->maxgeoms = ngeoms; } else { collection->geoms = NULL; collection->maxgeoms = 0; } /* Sub-geometries are never de-serialized with boxes (#1254) */ FLAGS_SET_BBOX(lwflags, 0); for ( i = 0; i < ngeoms; i++ ) { uint32_t subtype = gserialized1_get_uint32_t(data_ptr); size_t subsize = 0; if ( ! lwcollection_allows_subtype(type, subtype) ) { lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type)); lwfree(collection); return NULL; } collection->geoms[i] = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &subsize); data_ptr += subsize; } if ( size ) *size = data_ptr - start_ptr; return collection; } LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *g_size) { uint32_t type; assert(data_ptr); type = gserialized1_get_uint32_t(data_ptr); LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type), FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), FLAGS_GET_GEODETIC(lwflags), FLAGS_GET_BBOX(lwflags)); switch (type) { case POINTTYPE: return (LWGEOM *)lwpoint_from_gserialized1_buffer(data_ptr, lwflags, g_size); case LINETYPE: return (LWGEOM *)lwline_from_gserialized1_buffer(data_ptr, lwflags, g_size); case CIRCSTRINGTYPE: return (LWGEOM *)lwcircstring_from_gserialized1_buffer(data_ptr, lwflags, g_size); case POLYGONTYPE: return (LWGEOM *)lwpoly_from_gserialized1_buffer(data_ptr, lwflags, g_size); case TRIANGLETYPE: return (LWGEOM *)lwtriangle_from_gserialized1_buffer(data_ptr, lwflags, g_size); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_from_gserialized1_buffer(data_ptr, lwflags, g_size); default: lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type)); return NULL; } } LWGEOM* lwgeom_from_gserialized1(const GSERIALIZED *g) { lwflags_t lwflags = 0; int32_t srid = 0; uint32_t lwtype = 0; uint8_t *data_ptr = NULL; LWGEOM *lwgeom = NULL; GBOX bbox; size_t size = 0; assert(g); srid = gserialized1_get_srid(g); lwtype = gserialized1_get_type(g); lwflags = gserialized1_get_lwflags(g); LWDEBUGF(4, "Got type %d (%s), srid=%d", lwtype, lwtype_name(lwtype), srid); data_ptr = (uint8_t*)g->data; if (FLAGS_GET_BBOX(lwflags)) data_ptr += gbox_serialized_size(lwflags); lwgeom = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &size); if ( ! lwgeom ) lwerror("%s: unable create geometry", __func__); /* Ooops! */ lwgeom->type = lwtype; lwgeom->flags = lwflags; if ( gserialized1_read_gbox_p(g, &bbox) == LW_SUCCESS ) { lwgeom->bbox = gbox_copy(&bbox); } else if ( lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS) ) { lwgeom->bbox = gbox_copy(&bbox); } else { lwgeom->bbox = NULL; } lwgeom_set_srid(lwgeom, srid); return lwgeom; } const float * gserialized1_get_float_box_p(const GSERIALIZED *g, size_t *ndims) { if (ndims) *ndims = G1FLAGS_NDIMS_BOX(g->gflags); if (!g) return NULL; if (!G1FLAGS_GET_BBOX(g->gflags)) return NULL; return (const float *)(g->data); } /** * Update the bounding box of a #GSERIALIZED, allocating a fresh one * if there is not enough space to just write the new box in. * WARNING if a new object needs to be created, the * input pointer will have to be freed by the caller! Check * to see if input == output. Returns null if there's a problem * like mismatched dimensions. */ GSERIALIZED* gserialized1_set_gbox(GSERIALIZED *g, GBOX *gbox) { int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags); int box_ndims = FLAGS_NDIMS_BOX(gbox->flags); GSERIALIZED *g_out = NULL; size_t box_size = 2 * g_ndims * sizeof(float); float *fbox; int fbox_pos = 0; /* The dimensionality of the inputs has to match or we are SOL. */ if ( g_ndims != box_ndims ) { return NULL; } /* Serialized already has room for a box. */ if (G1FLAGS_GET_BBOX(g->gflags)) { g_out = g; } /* Serialized has no box. We need to allocate enough space for the old data plus the box, and leave a gap in the memory segment to write the new values into. */ else { size_t varsize_new = SIZE_GET(g->size) + box_size; uint8_t *ptr; g_out = lwalloc(varsize_new); /* Copy the head of g into place */ memcpy(g_out, g, 8); /* Copy the body of g into place after leaving space for the box */ ptr = g_out->data; ptr += box_size; memcpy(ptr, g->data, SIZE_GET(g->size) - 8); G1FLAGS_SET_BBOX(g_out->gflags, 1); SIZE_SET(g_out->size, varsize_new); } /* Move bounds to nearest float values */ gbox_float_round(gbox); /* Now write the float box values into the memory segement */ fbox = (float*)(g_out->data); /* Copy in X/Y */ fbox[fbox_pos++] = gbox->xmin; fbox[fbox_pos++] = gbox->xmax; fbox[fbox_pos++] = gbox->ymin; fbox[fbox_pos++] = gbox->ymax; /* Optionally copy in higher dims */ if(gserialized1_has_z(g) || gserialized1_is_geodetic(g)) { fbox[fbox_pos++] = gbox->zmin; fbox[fbox_pos++] = gbox->zmax; } if(gserialized1_has_m(g) && ! gserialized1_is_geodetic(g)) { fbox[fbox_pos++] = gbox->mmin; fbox[fbox_pos++] = gbox->mmax; } return g_out; } /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED* gserialized1_drop_gbox(GSERIALIZED *g) { int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags); size_t box_size = 2 * g_ndims * sizeof(float); size_t g_out_size = SIZE_GET(g->size) - box_size; GSERIALIZED *g_out = lwalloc(g_out_size); /* Copy the contents while omitting the box */ if ( G1FLAGS_GET_BBOX(g->gflags) ) { uint8_t *outptr = (uint8_t*)g_out; uint8_t *inptr = (uint8_t*)g; /* Copy the header (size+type) of g into place */ memcpy(outptr, inptr, 8); outptr += 8; inptr += 8 + box_size; /* Copy parts after the box into place */ memcpy(outptr, inptr, g_out_size - 8); G1FLAGS_SET_BBOX(g_out->gflags, 0); SIZE_SET(g_out->size, g_out_size); } /* No box? Nothing to do but copy and return. */ else { memcpy(g_out, g, g_out_size); } return g_out; } lwgeom/src/liblwgeom/measures.h0000644000176200001440000001411213773172540016321 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2010 Nicklas Avén * **********************************************************************/ /********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * Copyright 2010 Nicklas Avén * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #ifndef _MEASURES_H #define _MEASURES_H 1 #include "liblwgeom_internal.h" /* for the measure functions*/ #define DIST_MAX -1 #define DIST_MIN 1 /** * Structure used in distance-calculations */ typedef struct { double distance; /*the distance between p1 and p2*/ POINT2D p1; POINT2D p2; int mode; /*the direction of looking, if thedir = -1 then we look for maxdistance and if it is 1 then we look for mindistance*/ int twisted; /*To preserve the order of incoming points to match the first and second point in shortest and longest line*/ double tolerance; /*the tolerance for dwithin and dfullywithin*/ } DISTPTS; typedef struct { double themeasure; /*a value calculated to compare distances*/ int pnr; /*pointnumber. the ordernumber of the point*/ } LISTSTRUCT; /* * Preprocessing functions */ int lw_dist2d_comp(const LWGEOM *lw1, const LWGEOM *lw2, DISTPTS *dl); int lw_dist2d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl); int lw_dist2d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl); int lw_dist2d_check_overlap(LWGEOM *lwg1, LWGEOM *lwg2); int lw_dist2d_distribute_fast(LWGEOM *lwg1, LWGEOM *lwg2, DISTPTS *dl); /* * Brute force functions */ int lw_dist2d_pt_ptarray(const POINT2D *p, POINTARRAY *pa, DISTPTS *dl); int lw_dist2d_pt_ptarrayarc(const POINT2D *p, const POINTARRAY *pa, DISTPTS *dl); int lw_dist2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl); int lw_dist2d_ptarray_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl); int lw_dist2d_ptarrayarc_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl); int lw_dist2d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly, DISTPTS *dl); int lw_dist2d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS *dl); int lw_dist2d_point_line(LWPOINT *point, LWLINE *line, DISTPTS *dl); int lw_dist2d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS *dl); int lw_dist2d_point_circstring(LWPOINT *point, LWCIRCSTRING *circ, DISTPTS *dl); int lw_dist2d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS *dl); int lw_dist2d_point_curvepoly(LWPOINT *point, LWCURVEPOLY *poly, DISTPTS *dl); int lw_dist2d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS *dl); int lw_dist2d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS *dl); int lw_dist2d_line_circstring(LWLINE *line1, LWCIRCSTRING *line2, DISTPTS *dl); int lw_dist2d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS *dl); int lw_dist2d_line_curvepoly(LWLINE *line, LWCURVEPOLY *poly, DISTPTS *dl); int lw_dist2d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS *dl); int lw_dist2d_tri_circstring(LWTRIANGLE *tri, LWCIRCSTRING *line, DISTPTS *dl); int lw_dist2d_tri_poly(LWTRIANGLE *tri, LWPOLY *poly, DISTPTS *dl); int lw_dist2d_tri_curvepoly(LWTRIANGLE *tri, LWCURVEPOLY *poly, DISTPTS *dl); int lw_dist2d_circstring_circstring(LWCIRCSTRING *line1, LWCIRCSTRING *line2, DISTPTS *dl); int lw_dist2d_circstring_poly(LWCIRCSTRING *circ, LWPOLY *poly, DISTPTS *dl); int lw_dist2d_circstring_curvepoly(LWCIRCSTRING *circ, LWCURVEPOLY *poly, DISTPTS *dl); int lw_dist2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS *dl); int lw_dist2d_poly_curvepoly(LWPOLY *poly1, LWCURVEPOLY *curvepoly2, DISTPTS *dl); int lw_dist2d_curvepoly_curvepoly(LWCURVEPOLY *poly1, LWCURVEPOLY *poly2, DISTPTS *dl); /* * New faster distance calculations */ int lw_dist2d_pre_seg_seg(POINTARRAY *l1, POINTARRAY *l2,LISTSTRUCT *list1, LISTSTRUCT *list2,double k, DISTPTS *dl); int lw_dist2d_selected_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl); int struct_cmp_by_measure(const void *a, const void *b); int lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1,POINTARRAY *l2, DISTPTS *dl, GBOX *box1, GBOX *box2); /* * Distance calculation primitives. */ int lw_dist2d_pt_pt (const POINT2D *P, const POINT2D *Q, DISTPTS *dl); int lw_dist2d_pt_seg (const POINT2D *P, const POINT2D *A1, const POINT2D *A2, DISTPTS *dl); int lw_dist2d_pt_arc (const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, DISTPTS *dl); int lw_dist2d_seg_seg(const POINT2D *A1, const POINT2D *A2, const POINT2D *B1, const POINT2D *B2, DISTPTS *dl); int lw_dist2d_seg_arc(const POINT2D *A1, const POINT2D *A2, const POINT2D *B1, const POINT2D *B2, const POINT2D *B3, DISTPTS *dl); int lw_dist2d_arc_arc(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *B1, const POINT2D *B2, const POINT2D* B3, DISTPTS *dl); void lw_dist2d_distpts_init(DISTPTS *dl, int mode); /* * Length primitives */ double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3); /* * Geometry returning functions */ LWGEOM *lw_dist2d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode); LWGEOM *lw_dist2d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode); #endif /* !defined _MEASURES_H */ lwgeom/src/liblwgeom/gserialized2.h0000644000176200001440000001372513773172540017072 0ustar liggesusers/** * Macros for manipulating the core 'flags' byte. A uint8_t used as follows: */ #define G2FLAG_Z 0x01 #define G2FLAG_M 0x02 #define G2FLAG_BBOX 0x04 #define G2FLAG_GEODETIC 0x08 #define G2FLAG_EXTENDED 0x10 #define G2FLAG_RESERVED1 0x20 /* RESERVED FOR FUTURE USES */ #define G2FLAG_VER_0 0x40 #define G2FLAG_RESERVED2 0x80 /* RESERVED FOR FUTURE VERSIONS */ /** * Macros for the extended 'flags' uint64_t. */ #define G2FLAG_X_SOLID 0x00000001 #define G2FLAG_X_CHECKED_VALID 0x00000002 // To Be Implemented? #define G2FLAG_X_IS_VALID 0x00000004 // To Be Implemented? #define G2FLAG_X_HAS_HASH 0x00000008 // To Be Implemented? #define G2FLAGS_GET_VERSION(gflags) (((gflags) & G2FLAG_VER_0)>>6) #define G2FLAGS_GET_Z(gflags) ((gflags) & G2FLAG_Z) #define G2FLAGS_GET_M(gflags) (((gflags) & G2FLAG_M)>>1) #define G2FLAGS_GET_BBOX(gflags) (((gflags) & G2FLAG_BBOX)>>2) #define G2FLAGS_GET_GEODETIC(gflags) (((gflags) & G2FLAG_GEODETIC)>>3) #define G2FLAGS_GET_EXTENDED(gflags) (((gflags) & G2FLAG_EXTENDED)>>4) #define G2FLAGS_GET_UNUSED(gflags) (((gflags) & G2FLAG_UNUSED)>>5) #define G2FLAGS_SET_Z(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_Z) : ((gflags) & ~G2FLAG_Z)) #define G2FLAGS_SET_M(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_M) : ((gflags) & ~G2FLAG_M)) #define G2FLAGS_SET_BBOX(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_BBOX) : ((gflags) & ~G2FLAG_BBOX)) #define G2FLAGS_SET_GEODETIC(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_GEODETIC) : ((gflags) & ~G2FLAG_GEODETIC)) #define G2FLAGS_SET_EXTENDED(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_EXTENDED) : ((gflags) & ~G2FLAG_EXTENDED)) #define G2FLAGS_SET_VERSION(gflags, value) ((gflags) = (value) ? ((gflags) | G2FLAG_VER_0) : ((gflags) & ~G2FLAG_VER_0)) #define G2FLAGS_NDIMS(gflags) (2 + G2FLAGS_GET_Z(gflags) + G2FLAGS_GET_M(gflags)) #define G2FLAGS_GET_ZM(gflags) (G2FLAGS_GET_M(gflags) + G2FLAGS_GET_Z(gflags) * 2) #define G2FLAGS_NDIMS_BOX(gflags) (G2FLAGS_GET_GEODETIC(gflags) ? 3 : G2FLAGS_NDIMS(gflags)) uint8_t g2flags(int has_z, int has_m, int is_geodetic); uint8_t lwflags_get_g2flags(lwflags_t lwflags); /* * GSERIALIZED PUBLIC API */ /** * Read the flags from a #GSERIALIZED and return a standard lwflag * integer */ lwflags_t gserialized2_get_lwflags(const GSERIALIZED *g); /** * Copy a new bounding box into an existing gserialized. * If necessary a new #GSERIALIZED will be allocated. Test * that input != output before freeing input. */ GSERIALIZED *gserialized2_set_gbox(GSERIALIZED *g, GBOX *gbox); /** * Remove the bounding box from a #GSERIALIZED. Returns a freshly * allocated #GSERIALIZED every time. */ GSERIALIZED* gserialized2_drop_gbox(GSERIALIZED *g); /** * Read the box from the #GSERIALIZED or calculate it if necessary. * Return #LWFAILURE if box cannot be calculated (NULL or EMPTY * input). */ int gserialized2_get_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Read the box from the #GSERIALIZED or return #LWFAILURE if * box is unavailable. */ int gserialized2_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox); /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ uint32_t gserialized2_get_type(const GSERIALIZED *g); /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ uint32_t gserialized2_max_header_size(void); /** * Returns a hash code for the srid/type/geometry information * in the GSERIALIZED. Ignores metadata like flags and optional * boxes, etc. */ int32_t gserialized2_hash(const GSERIALIZED *g); /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ int32_t gserialized2_get_srid(const GSERIALIZED *g); /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ void gserialized2_set_srid(GSERIALIZED *g, int32_t srid); /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ int gserialized2_is_empty(const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ int gserialized2_has_bbox(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an extended flags section. */ int gserialized2_has_extended(const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a Z ordinate. */ int gserialized2_has_z(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an M ordinate. */ int gserialized2_has_m(const GSERIALIZED *gser); /** * Check if a #GSERIALIZED is a geography. */ int gserialized2_is_geodetic(const GSERIALIZED *gser); /** * Return the number of dimensions (2, 3, 4) in a geometry */ int gserialized2_ndims(const GSERIALIZED *gser); /** * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ GSERIALIZED* gserialized2_from_lwgeom(LWGEOM *geom, size_t *size); /** * Return the memory size a GSERIALIZED will occupy for a given LWGEOM. */ size_t gserialized2_from_lwgeom_size(const LWGEOM *geom); /** * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates * that are double aligned and suitable for direct reading using getPoint2d_p_ro */ LWGEOM* lwgeom_from_gserialized2(const GSERIALIZED *g); /** * Point into the float box area of the serialization */ const float * gserialized2_get_float_box_p(const GSERIALIZED *g, size_t *ndims); int gserialized2_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox); int gserialized2_peek_first_point(const GSERIALIZED *g, POINT4D *out_point); lwgeom/src/liblwgeom/measures.c0000644000176200001440000021255213773172540016324 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * Copyright 2010 Nicklas Avén * Copyright 2012 Paul Ramsey * Copyright 2019 Darafei Praliaskouski * **********************************************************************/ #include #include #include "measures.h" #include "lwgeom_log.h" /*------------------------------------------------------------------------------------------------------------ Initializing functions The functions starting the distance-calculation processses --------------------------------------------------------------------------------------------------------------*/ LWGEOM * lwgeom_closest_line(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist2d_distanceline(lw1, lw2, lw1->srid, DIST_MIN); } LWGEOM * lwgeom_furthest_line(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist2d_distanceline(lw1, lw2, lw1->srid, DIST_MAX); } LWGEOM * lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist2d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN); } LWGEOM * lwgeom_furthest_point(const LWGEOM *lw1, const LWGEOM *lw2) { return lw_dist2d_distancepoint(lw1, lw2, lw1->srid, DIST_MAX); } void lw_dist2d_distpts_init(DISTPTS *dl, int mode) { dl->twisted = -1; dl->p1.x = dl->p1.y = 0.0; dl->p2.x = dl->p2.y = 0.0; dl->mode = mode; dl->tolerance = 0.0; if (mode == DIST_MIN) dl->distance = FLT_MAX; else dl->distance = -1 * FLT_MAX; } /** Function initializing shortestline and longestline calculations. */ LWGEOM * lw_dist2d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) { double x1, x2, y1, y2; double initdistance = (mode == DIST_MIN ? FLT_MAX : -1.0); DISTPTS thedl; LWPOINT *lwpoints[2]; LWGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0.0; LWDEBUG(2, "lw_dist2d_distanceline is called"); if (!lw_dist2d_comp(lw1, lw2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } /*if thedl.distance is unchanged there where only empty geometries input*/ if (thedl.distance == initdistance) { LWDEBUG(3, "didn't find geometries to measure between, returning null"); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } else { x1 = thedl.p1.x; y1 = thedl.p1.y; x2 = thedl.p2.x; y2 = thedl.p2.y; lwpoints[0] = lwpoint_make2d(srid, x1, y1); lwpoints[1] = lwpoint_make2d(srid, x2, y2); result = (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); } return result; } /** Function initializing closestpoint calculations. */ LWGEOM * lw_dist2d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) { double x, y; DISTPTS thedl; double initdistance = FLT_MAX; LWGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0; LWDEBUG(2, "lw_dist2d_distancepoint is called"); if (!lw_dist2d_comp(lw1, lw2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ lwerror("Some unspecified error."); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } if (thedl.distance == initdistance) { LWDEBUG(3, "didn't find geometries to measure between, returning null"); result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); } else { x = thedl.p1.x; y = thedl.p1.y; result = (LWGEOM *)lwpoint_make2d(srid, x, y); } return result; } /** Function initializing max distance calculation */ double lwgeom_maxdistance2d(const LWGEOM *lw1, const LWGEOM *lw2) { LWDEBUG(2, "lwgeom_maxdistance2d is called"); return lwgeom_maxdistance2d_tolerance(lw1, lw2, 0.0); } /** Function handling max distance calculations and dfullywithin calculations. The difference is just the tolerance. */ double lwgeom_maxdistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) { /*double thedist;*/ DISTPTS thedl; LWDEBUG(2, "lwgeom_maxdistance2d_tolerance is called"); thedl.mode = DIST_MAX; thedl.distance = -1; thedl.tolerance = tolerance; if (lw_dist2d_comp(lw1, lw2, &thedl)) return thedl.distance; /*should never get here. all cases ought to be error handled earlier*/ lwerror("Some unspecified error."); return -1; } /** Function initializing min distance calculation */ double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2) { return lwgeom_mindistance2d_tolerance(lw1, lw2, 0.0); } /** Function handling min distance calculations and dwithin calculations. The difference is just the tolerance. */ double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) { DISTPTS thedl; LWDEBUG(2, "lwgeom_mindistance2d_tolerance is called"); thedl.mode = DIST_MIN; thedl.distance = FLT_MAX; thedl.tolerance = tolerance; if (lw_dist2d_comp(lw1, lw2, &thedl)) return thedl.distance; /*should never get here. all cases ought to be error handled earlier*/ lwerror("Some unspecified error."); return FLT_MAX; } /*------------------------------------------------------------------------------------------------------------ End of Initializing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Preprocessing functions Functions preparing geometries for distance-calculations --------------------------------------------------------------------------------------------------------------*/ /** This function just deserializes geometries Bboxes is not checked here since it is the subgeometries bboxes we will use anyway. */ int lw_dist2d_comp(const LWGEOM *lw1, const LWGEOM *lw2, DISTPTS *dl) { return lw_dist2d_recursive(lw1, lw2, dl); } static int lw_dist2d_is_collection(const LWGEOM *g) { /* Differs from lwgeom_is_collection by not treating CURVEPOLYGON as collection */ switch (g->type) { case TINTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COMPOUNDTYPE: case POLYHEDRALSURFACETYPE: return LW_TRUE; break; default: return LW_FALSE; } } /** This is a recursive function delivering every possible combination of subgeometries */ int lw_dist2d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl) { int i, j; int n1 = 1; int n2 = 1; LWGEOM *g1 = NULL; LWGEOM *g2 = NULL; LWCOLLECTION *c1 = NULL; LWCOLLECTION *c2 = NULL; LWDEBUGF(2, "lw_dist2d_comp is called with type1=%d, type2=%d", lwg1->type, lwg2->type); if (lw_dist2d_is_collection(lwg1)) { LWDEBUG(3, "First geometry is collection"); c1 = lwgeom_as_lwcollection(lwg1); n1 = c1->ngeoms; } if (lw_dist2d_is_collection(lwg2)) { LWDEBUG(3, "Second geometry is collection"); c2 = lwgeom_as_lwcollection(lwg2); n2 = c2->ngeoms; } for (i = 0; i < n1; i++) { if (lw_dist2d_is_collection(lwg1)) g1 = c1->geoms[i]; else g1 = (LWGEOM *)lwg1; if (lwgeom_is_empty(g1)) return LW_TRUE; if (lw_dist2d_is_collection(g1)) { LWDEBUG(3, "Found collection inside first geometry collection, recursing"); if (!lw_dist2d_recursive(g1, lwg2, dl)) return LW_FALSE; continue; } for (j = 0; j < n2; j++) { if (lw_dist2d_is_collection(lwg2)) g2 = c2->geoms[j]; else g2 = (LWGEOM *)lwg2; if (lw_dist2d_is_collection(g2)) { LWDEBUG(3, "Found collection inside second geometry collection, recursing"); if (!lw_dist2d_recursive(g1, g2, dl)) return LW_FALSE; continue; } if (!g1->bbox) lwgeom_add_bbox(g1); if (!g2->bbox) lwgeom_add_bbox(g2); /* If one of geometries is empty, return. True here only means continue searching. False would * have stopped the process*/ if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2)) return LW_TRUE; if ((dl->mode != DIST_MAX) && (!lw_dist2d_check_overlap(g1, g2)) && (g1->type == LINETYPE || g1->type == POLYGONTYPE || g1->type == TRIANGLETYPE) && (g2->type == LINETYPE || g2->type == POLYGONTYPE || g2->type == TRIANGLETYPE)) { if (!lw_dist2d_distribute_fast(g1, g2, dl)) return LW_FALSE; } else { if (!lw_dist2d_distribute_bruteforce(g1, g2, dl)) return LW_FALSE; if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ } } } return LW_TRUE; } int lw_dist2d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl) { int t1 = lwg1->type; int t2 = lwg2->type; switch (t1) { case POINTTYPE: { dl->twisted = 1; switch (t2) { case POINTTYPE: return lw_dist2d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl); case LINETYPE: return lw_dist2d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl); case TRIANGLETYPE: return lw_dist2d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl); case POLYGONTYPE: return lw_dist2d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl); case CIRCSTRINGTYPE: return lw_dist2d_point_circstring((LWPOINT *)lwg1, (LWCIRCSTRING *)lwg2, dl); case CURVEPOLYTYPE: return lw_dist2d_point_curvepoly((LWPOINT *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } case LINETYPE: { dl->twisted = 1; switch (t2) { case POINTTYPE: dl->twisted = -1; return lw_dist2d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl); case LINETYPE: return lw_dist2d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl); case TRIANGLETYPE: return lw_dist2d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl); case POLYGONTYPE: return lw_dist2d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl); case CIRCSTRINGTYPE: return lw_dist2d_line_circstring((LWLINE *)lwg1, (LWCIRCSTRING *)lwg2, dl); case CURVEPOLYTYPE: return lw_dist2d_line_curvepoly((LWLINE *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } case TRIANGLETYPE: { dl->twisted = 1; switch (t2) { case POINTTYPE: dl->twisted = -1; return lw_dist2d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl); case LINETYPE: dl->twisted = -1; return lw_dist2d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl); case TRIANGLETYPE: return lw_dist2d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl); case POLYGONTYPE: return lw_dist2d_tri_poly((LWTRIANGLE *)lwg1, (LWPOLY *)lwg2, dl); case CIRCSTRINGTYPE: return lw_dist2d_tri_circstring((LWTRIANGLE *)lwg1, (LWCIRCSTRING *)lwg2, dl); case CURVEPOLYTYPE: return lw_dist2d_tri_curvepoly((LWTRIANGLE *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } case CIRCSTRINGTYPE: { dl->twisted = 1; switch (t2) { case POINTTYPE: dl->twisted = -1; return lw_dist2d_point_circstring((LWPOINT *)lwg2, (LWCIRCSTRING *)lwg1, dl); case LINETYPE: dl->twisted = -1; return lw_dist2d_line_circstring((LWLINE *)lwg2, (LWCIRCSTRING *)lwg1, dl); case TRIANGLETYPE: dl->twisted = -1; return lw_dist2d_tri_circstring((LWTRIANGLE *)lwg2, (LWCIRCSTRING *)lwg1, dl); case POLYGONTYPE: return lw_dist2d_circstring_poly((LWCIRCSTRING *)lwg1, (LWPOLY *)lwg2, dl); case CIRCSTRINGTYPE: return lw_dist2d_circstring_circstring((LWCIRCSTRING *)lwg1, (LWCIRCSTRING *)lwg2, dl); case CURVEPOLYTYPE: return lw_dist2d_circstring_curvepoly((LWCIRCSTRING *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } case POLYGONTYPE: { dl->twisted = -1; switch (t2) { case POINTTYPE: return lw_dist2d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl); case LINETYPE: return lw_dist2d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl); case TRIANGLETYPE: return lw_dist2d_tri_poly((LWTRIANGLE *)lwg2, (LWPOLY *)lwg1, dl); case CIRCSTRINGTYPE: return lw_dist2d_circstring_poly((LWCIRCSTRING *)lwg2, (LWPOLY *)lwg1, dl); case POLYGONTYPE: dl->twisted = 1; return lw_dist2d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl); case CURVEPOLYTYPE: dl->twisted = 1; return lw_dist2d_poly_curvepoly((LWPOLY *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } case CURVEPOLYTYPE: { dl->twisted = -1; switch (t2) { case POINTTYPE: return lw_dist2d_point_curvepoly((LWPOINT *)lwg2, (LWCURVEPOLY *)lwg1, dl); case LINETYPE: return lw_dist2d_line_curvepoly((LWLINE *)lwg2, (LWCURVEPOLY *)lwg1, dl); case TRIANGLETYPE: return lw_dist2d_tri_curvepoly((LWTRIANGLE *)lwg2, (LWCURVEPOLY *)lwg1, dl); case POLYGONTYPE: return lw_dist2d_poly_curvepoly((LWPOLY *)lwg2, (LWCURVEPOLY *)lwg1, dl); case CIRCSTRINGTYPE: return lw_dist2d_circstring_curvepoly((LWCIRCSTRING *)lwg2, (LWCURVEPOLY *)lwg1, dl); case CURVEPOLYTYPE: dl->twisted = 1; return lw_dist2d_curvepoly_curvepoly((LWCURVEPOLY *)lwg1, (LWCURVEPOLY *)lwg2, dl); default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); return LW_FALSE; } } default: { lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1)); return LW_FALSE; } } return LW_FALSE; } /* Check for overlapping bboxes */ int lw_dist2d_check_overlap(LWGEOM *lwg1, LWGEOM *lwg2) { LWDEBUG(2, "lw_dist2d_check_overlap is called"); if (!lwg1->bbox) lwgeom_calculate_gbox(lwg1, lwg1->bbox); if (!lwg2->bbox) lwgeom_calculate_gbox(lwg2, lwg2->bbox); /* Check if the geometries intersect. */ if ((lwg1->bbox->xmax < lwg2->bbox->xmin || lwg1->bbox->xmin > lwg2->bbox->xmax || lwg1->bbox->ymax < lwg2->bbox->ymin || lwg1->bbox->ymin > lwg2->bbox->ymax)) { LWDEBUG(3, "geometries bboxes did not overlap"); return LW_FALSE; } LWDEBUG(3, "geometries bboxes overlap"); return LW_TRUE; } /** Geometries are distributed for the new faster distance-calculations */ int lw_dist2d_distribute_fast(LWGEOM *lwg1, LWGEOM *lwg2, DISTPTS *dl) { POINTARRAY *pa1, *pa2; int type1 = lwg1->type; int type2 = lwg2->type; LWDEBUGF(2, "lw_dist2d_distribute_fast is called with typ1=%d, type2=%d", lwg1->type, lwg2->type); switch (type1) { case LINETYPE: pa1 = ((LWLINE *)lwg1)->points; break; case POLYGONTYPE: pa1 = ((LWPOLY *)lwg1)->rings[0]; break; case TRIANGLETYPE: pa1 = ((LWTRIANGLE *)lwg1)->points; break; default: lwerror("Unsupported geometry1 type: %s", lwtype_name(type1)); return LW_FALSE; } switch (type2) { case LINETYPE: pa2 = ((LWLINE *)lwg2)->points; break; case POLYGONTYPE: pa2 = ((LWPOLY *)lwg2)->rings[0]; break; case TRIANGLETYPE: pa2 = ((LWTRIANGLE *)lwg2)->points; break; default: lwerror("Unsupported geometry2 type: %s", lwtype_name(type1)); return LW_FALSE; } dl->twisted = 1; return lw_dist2d_fast_ptarray_ptarray(pa1, pa2, dl, lwg1->bbox, lwg2->bbox); } /*------------------------------------------------------------------------------------------------------------ End of Preprocessing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Brute force functions The old way of calculating distances, now used for: 1) distances to points (because there shouldn't be anything to gain by the new way of doing it) 2) distances when subgeometries geometries bboxes overlaps --------------------------------------------------------------------------------------------------------------*/ /** point to point calculation */ int lw_dist2d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS *dl) { const POINT2D *p1 = getPoint2d_cp(point1->point, 0); const POINT2D *p2 = getPoint2d_cp(point2->point, 0); return lw_dist2d_pt_pt(p1, p2, dl); } /** point to line calculation */ int lw_dist2d_point_line(LWPOINT *point, LWLINE *line, DISTPTS *dl) { const POINT2D *p = getPoint2d_cp(point->point, 0); return lw_dist2d_pt_ptarray(p, line->points, dl); } int lw_dist2d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS *dl) { const POINT2D *pt = getPoint2d_cp(point->point, 0); /* Is point inside triangle? */ if (dl->mode == DIST_MIN && ptarray_contains_point(tri->points, pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } return lw_dist2d_pt_ptarray(pt, tri->points, dl); } int lw_dist2d_point_circstring(LWPOINT *point, LWCIRCSTRING *circ, DISTPTS *dl) { const POINT2D *p; p = getPoint2d_cp(point->point, 0); return lw_dist2d_pt_ptarrayarc(p, circ->points, dl); } /** * 1. see if pt in outer boundary. if no, then treat the outer ring like a line * 2. if in the boundary, test to see if its in a hole. * if so, then return dist to hole, else return 0 (point in polygon) */ int lw_dist2d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS *dl) { const POINT2D *p = getPoint2d_cp(point->point, 0); /* Max distance? Check only outer ring.*/ if (dl->mode == DIST_MAX) return lw_dist2d_pt_ptarray(p, poly->rings[0], dl); /* Return distance to outer ring if not inside it */ if (ptarray_contains_point(poly->rings[0], p) == LW_OUTSIDE) return lw_dist2d_pt_ptarray(p, poly->rings[0], dl); /* * Inside the outer ring. * Scan though each of the inner rings looking to * see if its inside. If not, distance==0. * Otherwise, distance = pt to ring distance */ for (uint32_t i = 1; i < poly->nrings; i++) if (ptarray_contains_point(poly->rings[i], p) != LW_OUTSIDE) return lw_dist2d_pt_ptarray(p, poly->rings[i], dl); /* Is inside the polygon */ dl->distance = 0.0; dl->p1.x = dl->p2.x = p->x; dl->p1.y = dl->p2.y = p->y; return LW_TRUE; } int lw_dist2d_point_curvepoly(LWPOINT *point, LWCURVEPOLY *poly, DISTPTS *dl) { const POINT2D *p = getPoint2d_cp(point->point, 0); if (dl->mode == DIST_MAX) lwerror("lw_dist2d_point_curvepoly cannot calculate max distance"); /* Return distance to outer ring if not inside it */ if (lwgeom_contains_point(poly->rings[0], p) == LW_OUTSIDE) return lw_dist2d_recursive((LWGEOM *)point, poly->rings[0], dl); /* Inside the outer ring. * Scan though each of the inner rings looking to see if its inside. If not, distance==0. * Otherwise, distance = pt to ring distance. */ for (uint32_t i = 1; i < poly->nrings; i++) if (lwgeom_contains_point(poly->rings[i], p) == LW_INSIDE) return lw_dist2d_recursive((LWGEOM *)point, poly->rings[i], dl); /* Is inside the polygon */ dl->distance = 0.0; dl->p1.x = dl->p2.x = p->x; dl->p1.y = dl->p2.y = p->y; return LW_TRUE; } int lw_dist2d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS *dl) { POINTARRAY *pa1 = line1->points; POINTARRAY *pa2 = line2->points; return lw_dist2d_ptarray_ptarray(pa1, pa2, dl); } int lw_dist2d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS *dl) { const POINT2D *pt = getPoint2d_cp(line->points, 0); /* Is there a point inside triangle? */ if (dl->mode == DIST_MIN && ptarray_contains_point(tri->points, pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } return lw_dist2d_ptarray_ptarray(line->points, tri->points, dl); } int lw_dist2d_line_circstring(LWLINE *line1, LWCIRCSTRING *line2, DISTPTS *dl) { return lw_dist2d_ptarray_ptarrayarc(line1->points, line2->points, dl); } /** * line to polygon calculation * Brute force. * Test line-ring distance against each ring. * If there's an intersection (distance==0) then return 0 (crosses boundary). * Otherwise, test to see if any point is inside outer rings of polygon, * but not in inner rings. * If so, return 0 (line inside polygon), * otherwise return min distance to a ring (could be outside * polygon or inside a hole) */ int lw_dist2d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS *dl) { POINTARRAY *pa = line->points; const POINT2D *pt = getPoint2d_cp(pa, 0); /* Line has a pount outside poly. Check distance to outer ring only. */ if (ptarray_contains_point(poly->rings[0], pt) == LW_OUTSIDE) return lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl); for (uint32_t i = 1; i < poly->nrings; i++) { if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[i], dl)) return LW_FALSE; /* just a check if the answer is already given */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; } /* It's inside a hole, then the actual distance is the min ring distance */ for (uint32_t i = 1; i < poly->nrings; i++) if (ptarray_contains_point(poly->rings[i], pt) != LW_OUTSIDE) return LW_TRUE; /* Not in hole, so inside polygon */ if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; } return LW_TRUE; } int lw_dist2d_line_curvepoly(LWLINE *line, LWCURVEPOLY *poly, DISTPTS *dl) { const POINT2D *pt = getPoint2d_cp(line->points, 0); /* Line has a pount outside curvepoly. Check distance to outer ring only. */ if (lwgeom_contains_point(poly->rings[0], pt) == LW_OUTSIDE) return lw_dist2d_recursive((LWGEOM *)line, poly->rings[0], dl); for (uint32_t i = 1; i < poly->nrings; i++) { if (!lw_dist2d_recursive((LWGEOM *)line, poly->rings[i], dl)) return LW_FALSE; /* just a check if the answer is already given */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; } /* It's inside a hole, then distance is actual min distance */ for (uint32_t i = 1; i < poly->nrings; i++) if (lwgeom_contains_point(poly->rings[i], pt) != LW_OUTSIDE) return LW_TRUE; /* Not in hole, so inside polygon */ if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; } return LW_TRUE; } int lw_dist2d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS *dl) { POINTARRAY *pa1 = tri1->points; POINTARRAY *pa2 = tri2->points; const POINT2D *pt = getPoint2d_cp(pa2, 0); if (dl->mode == DIST_MIN && ptarray_contains_point(pa1, pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } pt = getPoint2d_cp(pa1, 0); if (dl->mode == DIST_MIN && ptarray_contains_point(pa2, pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } return lw_dist2d_ptarray_ptarray(pa1, pa2, dl); } int lw_dist2d_tri_poly(LWTRIANGLE *tri, LWPOLY *poly, DISTPTS *dl) { POINTARRAY *pa = tri->points; const POINT2D *pt = getPoint2d_cp(pa, 0); /* If we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) return lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl); /* Triangle has a point outside poly. Check distance to outer ring only. */ if (ptarray_contains_point(poly->rings[0], pt) == LW_OUTSIDE) { if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl)) return LW_FALSE; /* just a check if the answer is already given */ if (dl->distance <= dl->tolerance) return LW_TRUE; /* Maybe poly is inside triangle? */ const POINT2D *pt2 = getPoint2d_cp(poly->rings[0], 0); if (ptarray_contains_point(pa, pt2) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt2->x; dl->p1.y = dl->p2.y = pt2->y; return LW_TRUE; } } for (uint32_t i = 1; i < poly->nrings; i++) { if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[i], dl)) return LW_FALSE; /* just a check if the answer is already given */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; } /* It's inside a hole, then the actual distance is the min ring distance */ for (uint32_t i = 1; i < poly->nrings; i++) if (ptarray_contains_point(poly->rings[i], pt) != LW_OUTSIDE) return LW_TRUE; /* Not in hole, so inside polygon */ dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } static const POINT2D * lw_curvering_getfirstpoint2d_cp(LWGEOM *geom) { switch (geom->type) { case LINETYPE: return getPoint2d_cp(((LWLINE *)geom)->points, 0); case CIRCSTRINGTYPE: return getPoint2d_cp(((LWCIRCSTRING *)geom)->points, 0); case COMPOUNDTYPE: { LWCOMPOUND *comp = (LWCOMPOUND *)geom; LWLINE *line = (LWLINE *)(comp->geoms[0]); return getPoint2d_cp(line->points, 0); } default: lwerror("lw_curvering_getfirstpoint2d_cp: unknown type"); } return NULL; } int lw_dist2d_tri_curvepoly(LWTRIANGLE *tri, LWCURVEPOLY *poly, DISTPTS *dl) { const POINT2D *pt = getPoint2d_cp(tri->points, 0); /* If we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) return lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl); /* Line has a pount outside curvepoly. Check distance to outer ring only. */ if (lwgeom_contains_point(poly->rings[0], pt) == LW_OUTSIDE) { if (lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl)) return LW_TRUE; /* Maybe poly is inside triangle? */ if (lwgeom_contains_point((LWGEOM *)tri, lw_curvering_getfirstpoint2d_cp(poly->rings[0])) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } } for (uint32_t i = 1; i < poly->nrings; i++) { if (!lw_dist2d_recursive((LWGEOM *)tri, poly->rings[i], dl)) return LW_FALSE; /* just a check if the answer is already given */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; } /* It's inside a hole, then distance is actual min distance */ for (uint32_t i = 1; i < poly->nrings; i++) if (lwgeom_contains_point(poly->rings[i], pt) != LW_OUTSIDE) return LW_TRUE; /* Not in hole, so inside polygon */ dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } int lw_dist2d_tri_circstring(LWTRIANGLE *tri, LWCIRCSTRING *line, DISTPTS *dl) { const POINT2D *pt = lw_curvering_getfirstpoint2d_cp((LWGEOM *)line); if (ptarray_contains_point(tri->points, pt) != LW_OUTSIDE && dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } return lw_dist2d_ptarray_ptarrayarc(tri->points, line->points, dl); } /** Function handling polygon to polygon calculation 1 if we are looking for maxdistance, just check the outer rings. 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings 3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole of poly1 4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole of poly2 5 If we have come all the way here we know that the first point of one of them is inside the other ones outer ring and not in holes so we check which one is inside. */ int lw_dist2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS *dl) { const POINT2D *pt; LWDEBUG(2, "lw_dist2d_poly_poly called"); /*1 if we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); /* 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings here it would be possible to handle the information about which one is inside which one and only search for the smaller ones in the bigger ones holes.*/ pt = getPoint2d_cp(poly1->rings[0], 0); if (ptarray_contains_point(poly2->rings[0], pt) == LW_OUTSIDE) { pt = getPoint2d_cp(poly2->rings[0], 0); if (ptarray_contains_point(poly1->rings[0], pt) == LW_OUTSIDE) return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); } /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole * of poly1*/ pt = getPoint2d_cp(poly2->rings[0], 0); for (uint32_t i = 1; i < poly1->nrings; i++) if (ptarray_contains_point(poly1->rings[i], pt) != LW_OUTSIDE) return lw_dist2d_ptarray_ptarray(poly1->rings[i], poly2->rings[0], dl); /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole * of poly2*/ pt = getPoint2d_cp(poly1->rings[0], 0); for (uint32_t i = 1; i < poly2->nrings; i++) if (ptarray_contains_point(poly2->rings[i], pt) != LW_OUTSIDE) return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[i], dl); /*5 If we have come all the way here we know that the first point of one of them is inside the other ones * outer ring and not in holes so we check wich one is inside.*/ pt = getPoint2d_cp(poly1->rings[0], 0); if (ptarray_contains_point(poly2->rings[0], pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } pt = getPoint2d_cp(poly2->rings[0], 0); if (ptarray_contains_point(poly1->rings[0], pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } lwerror("Unspecified error in function lw_dist2d_poly_poly"); return LW_FALSE; } int lw_dist2d_poly_curvepoly(LWPOLY *poly1, LWCURVEPOLY *curvepoly2, DISTPTS *dl) { LWCURVEPOLY *curvepoly1 = lwcurvepoly_construct_from_lwpoly(poly1); int rv = lw_dist2d_curvepoly_curvepoly(curvepoly1, curvepoly2, dl); lwgeom_free((LWGEOM *)curvepoly1); return rv; } int lw_dist2d_circstring_poly(LWCIRCSTRING *circ, LWPOLY *poly, DISTPTS *dl) { LWCURVEPOLY *curvepoly = lwcurvepoly_construct_from_lwpoly(poly); int rv = lw_dist2d_line_curvepoly((LWLINE *)circ, curvepoly, dl); lwgeom_free((LWGEOM *)curvepoly); return rv; } int lw_dist2d_circstring_curvepoly(LWCIRCSTRING *circ, LWCURVEPOLY *poly, DISTPTS *dl) { return lw_dist2d_line_curvepoly((LWLINE *)circ, poly, dl); } int lw_dist2d_circstring_circstring(LWCIRCSTRING *line1, LWCIRCSTRING *line2, DISTPTS *dl) { return lw_dist2d_ptarrayarc_ptarrayarc(line1->points, line2->points, dl); } int lw_dist2d_curvepoly_curvepoly(LWCURVEPOLY *poly1, LWCURVEPOLY *poly2, DISTPTS *dl) { const POINT2D *pt; /*1 if we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl); /* 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings here it would be possible to handle the information about which one is inside which one and only search for the smaller ones in the bigger ones holes.*/ pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]); if (lwgeom_contains_point(poly2->rings[0], pt) == LW_OUTSIDE) { pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]); if (lwgeom_contains_point(poly1->rings[0], pt) == LW_OUTSIDE) return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl); } /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole * of poly1*/ pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]); for (uint32_t i = 1; i < poly1->nrings; i++) if (lwgeom_contains_point(poly1->rings[i], pt) != LW_OUTSIDE) return lw_dist2d_recursive(poly1->rings[i], poly2->rings[0], dl); /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole * of poly2*/ pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]); for (uint32_t i = 1; i < poly2->nrings; i++) if (lwgeom_contains_point(poly2->rings[i], pt) != LW_OUTSIDE) return lw_dist2d_recursive(poly1->rings[0], poly2->rings[i], dl); /*5 If we have come all the way here we know that the first point of one of them is inside the other ones * outer ring and not in holes so we check which one is inside.*/ pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]); if (lwgeom_contains_point(poly2->rings[0], pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]); if (lwgeom_contains_point(poly1->rings[0], pt) != LW_OUTSIDE) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return LW_TRUE; } lwerror("Unspecified error in function lw_dist2d_curvepoly_curvepoly"); return LW_FALSE; } /** * search all the segments of pointarray to see which one is closest to p1 * Returns minimum distance between point and pointarray */ int lw_dist2d_pt_ptarray(const POINT2D *p, POINTARRAY *pa, DISTPTS *dl) { const POINT2D *start, *end; int twist = dl->twisted; start = getPoint2d_cp(pa, 0); if (!lw_dist2d_pt_pt(p, start, dl)) return LW_FALSE; for (uint32_t t = 1; t < pa->npoints; t++) { dl->twisted = twist; end = getPoint2d_cp(pa, t); if (!lw_dist2d_pt_seg(p, start, end, dl)) return LW_FALSE; if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ start = end; } return LW_TRUE; } /** * Search all the arcs of pointarray to see which one is closest to p1 * Returns minimum distance between point and arc pointarray. */ int lw_dist2d_pt_ptarrayarc(const POINT2D *p, const POINTARRAY *pa, DISTPTS *dl) { uint32_t t; const POINT2D *A1; const POINT2D *A2; const POINT2D *A3; int twist = dl->twisted; LWDEBUG(2, "lw_dist2d_pt_ptarrayarc is called"); if (pa->npoints % 2 == 0 || pa->npoints < 3) { lwerror("lw_dist2d_pt_ptarrayarc called with non-arc input"); return LW_FALSE; } if (dl->mode == DIST_MAX) { lwerror("lw_dist2d_pt_ptarrayarc does not currently support DIST_MAX mode"); return LW_FALSE; } A1 = getPoint2d_cp(pa, 0); if (!lw_dist2d_pt_pt(p, A1, dl)) return LW_FALSE; for (t = 1; t < pa->npoints; t += 2) { dl->twisted = twist; A2 = getPoint2d_cp(pa, t); A3 = getPoint2d_cp(pa, t + 1); if (lw_dist2d_pt_arc(p, A1, A2, A3, dl) == LW_FALSE) return LW_FALSE; if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ A1 = A3; } return LW_TRUE; } /** * test each segment of l1 against each segment of l2. */ int lw_dist2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl) { uint32_t t, u; const POINT2D *start, *end; const POINT2D *start2, *end2; int twist = dl->twisted; LWDEBUGF(2, "lw_dist2d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints); /* If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have * to be between two vertexes*/ if (dl->mode == DIST_MAX) { for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */ { start = getPoint2d_cp(l1, t); for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */ { start2 = getPoint2d_cp(l2, u); lw_dist2d_pt_pt(start, start2, dl); } } } else { start = getPoint2d_cp(l1, 0); for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */ { end = getPoint2d_cp(l1, t); start2 = getPoint2d_cp(l2, 0); for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */ { end2 = getPoint2d_cp(l2, u); dl->twisted = twist; lw_dist2d_seg_seg(start, end, start2, end2, dl); if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ start2 = end2; } start = end; } } return LW_TRUE; } /** * Test each segment of pa against each arc of pb for distance. */ int lw_dist2d_ptarray_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl) { uint32_t t, u; const POINT2D *A1; const POINT2D *A2; const POINT2D *B1; const POINT2D *B2; const POINT2D *B3; int twist = dl->twisted; LWDEBUGF(2, "lw_dist2d_ptarray_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints); if (pb->npoints % 2 == 0 || pb->npoints < 3) { lwerror("lw_dist2d_ptarray_ptarrayarc called with non-arc input"); return LW_FALSE; } if (dl->mode == DIST_MAX) { lwerror("lw_dist2d_ptarray_ptarrayarc does not currently support DIST_MAX mode"); return LW_FALSE; } else { A1 = getPoint2d_cp(pa, 0); for (t = 1; t < pa->npoints; t++) /* For each segment in pa */ { A2 = getPoint2d_cp(pa, t); B1 = getPoint2d_cp(pb, 0); for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */ { B2 = getPoint2d_cp(pb, u); B3 = getPoint2d_cp(pb, u + 1); dl->twisted = twist; lw_dist2d_seg_arc(A1, A2, B1, B2, B3, dl); /* If we've found a distance within tolerance, we're done */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; B1 = B3; } A1 = A2; } } return LW_TRUE; } /** * Test each arc of pa against each arc of pb for distance. */ int lw_dist2d_ptarrayarc_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl) { uint32_t t, u; const POINT2D *A1; const POINT2D *A2; const POINT2D *A3; const POINT2D *B1; const POINT2D *B2; const POINT2D *B3; int twist = dl->twisted; LWDEBUGF(2, "lw_dist2d_ptarrayarc_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints); if (dl->mode == DIST_MAX) { lwerror("lw_dist2d_ptarrayarc_ptarrayarc does not currently support DIST_MAX mode"); return LW_FALSE; } else { A1 = getPoint2d_cp(pa, 0); for (t = 1; t < pa->npoints; t += 2) /* For each segment in pa */ { A2 = getPoint2d_cp(pa, t); A3 = getPoint2d_cp(pa, t + 1); B1 = getPoint2d_cp(pb, 0); for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */ { B2 = getPoint2d_cp(pb, u); B3 = getPoint2d_cp(pb, u + 1); dl->twisted = twist; lw_dist2d_arc_arc(A1, A2, A3, B1, B2, B3, dl); /* If we've found a distance within tolerance, we're done */ if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; B1 = B3; } A1 = A3; } } return LW_TRUE; } /** * Calculate the shortest distance between an arc and an edge. * Line/circle approach from http://stackoverflow.com/questions/1073336/circle-line-collision-detection */ int lw_dist2d_seg_arc(const POINT2D *A1, const POINT2D *A2, const POINT2D *B1, const POINT2D *B2, const POINT2D *B3, DISTPTS *dl) { POINT2D C; /* center of arc circle */ double radius_C; /* radius of arc circle */ POINT2D D; /* point on A closest to C */ double dist_C_D; /* distance from C to D */ int pt_in_arc, pt_in_seg; DISTPTS dltmp; /* Bail out on crazy modes */ if (dl->mode < 0) lwerror("lw_dist2d_seg_arc does not support maxdistance mode"); /* What if the "arc" is a point? */ if (lw_arc_is_pt(B1, B2, B3)) return lw_dist2d_pt_seg(B1, A1, A2, dl); /* Calculate center and radius of the circle. */ radius_C = lw_arc_center(B1, B2, B3, &C); /* This "arc" is actually a line (B2 is collinear with B1,B3) */ if (radius_C < 0.0) return lw_dist2d_seg_seg(A1, A2, B1, B3, dl); /* Calculate distance between the line and circle center */ lw_dist2d_distpts_init(&dltmp, DIST_MIN); if (lw_dist2d_pt_seg(&C, A1, A2, &dltmp) == LW_FALSE) lwerror("lw_dist2d_pt_seg failed in lw_dist2d_seg_arc"); D = dltmp.p1; dist_C_D = dltmp.distance; /* Line intersects circle, maybe arc intersects edge? */ /* If so, that's the closest point. */ /* If not, the closest point is one of the end points of A */ if (dist_C_D < radius_C) { double length_A; /* length of the segment A */ POINT2D E, F; /* points of intersection of edge A and circle(B) */ double dist_D_EF; /* distance from D to E or F (same distance both ways) */ dist_D_EF = sqrt(radius_C * radius_C - dist_C_D * dist_C_D); length_A = sqrt((A2->x - A1->x) * (A2->x - A1->x) + (A2->y - A1->y) * (A2->y - A1->y)); /* Point of intersection E */ E.x = D.x - (A2->x - A1->x) * dist_D_EF / length_A; E.y = D.y - (A2->y - A1->y) * dist_D_EF / length_A; /* Point of intersection F */ F.x = D.x + (A2->x - A1->x) * dist_D_EF / length_A; F.y = D.y + (A2->y - A1->y) * dist_D_EF / length_A; /* If E is within A and within B then it's an intersection point */ pt_in_arc = lw_pt_in_arc(&E, B1, B2, B3); pt_in_seg = lw_pt_in_seg(&E, A1, A2); if (pt_in_arc && pt_in_seg) { dl->distance = 0.0; dl->p1 = E; dl->p2 = E; return LW_TRUE; } /* If F is within A and within B then it's an intersection point */ pt_in_arc = lw_pt_in_arc(&F, B1, B2, B3); pt_in_seg = lw_pt_in_seg(&F, A1, A2); if (pt_in_arc && pt_in_seg) { dl->distance = 0.0; dl->p1 = F; dl->p2 = F; return LW_TRUE; } } /* Line grazes circle, maybe arc intersects edge? */ /* If so, grazing point is the closest point. */ /* If not, the closest point is one of the end points of A */ else if (dist_C_D == radius_C) { /* Closest point D is also the point of grazing */ pt_in_arc = lw_pt_in_arc(&D, B1, B2, B3); pt_in_seg = lw_pt_in_seg(&D, A1, A2); /* Is D contained in both A and B? */ if (pt_in_arc && pt_in_seg) { dl->distance = 0.0; dl->p1 = D; dl->p2 = D; return LW_TRUE; } } /* Line misses circle. */ /* If closest point to A on circle is within B, then that's the closest */ /* Otherwise, the closest point will be an end point of A */ else { POINT2D G; /* Point on circle closest to A */ G.x = C.x + (D.x - C.x) * radius_C / dist_C_D; G.y = C.y + (D.y - C.y) * radius_C / dist_C_D; pt_in_arc = lw_pt_in_arc(&G, B1, B2, B3); pt_in_seg = lw_pt_in_seg(&D, A1, A2); /* Closest point is on the interior of A and B */ if (pt_in_arc && pt_in_seg) return lw_dist2d_pt_pt(&D, &G, dl); } /* Now we test the many combinations of end points with either */ /* arcs or edges. Each previous check determined if the closest */ /* potential point was within the arc/segment inscribed on the */ /* line/circle holding the arc/segment. */ /* Closest point is in the arc, but not in the segment, so */ /* one of the segment end points must be the closest. */ if (pt_in_arc && !pt_in_seg) { lw_dist2d_pt_arc(A1, B1, B2, B3, dl); lw_dist2d_pt_arc(A2, B1, B2, B3, dl); return LW_TRUE; } /* or, one of the arc end points is the closest */ else if (pt_in_seg && !pt_in_arc) { lw_dist2d_pt_seg(B1, A1, A2, dl); lw_dist2d_pt_seg(B3, A1, A2, dl); return LW_TRUE; } /* Finally, one of the end-point to end-point combos is the closest. */ else { lw_dist2d_pt_pt(A1, B1, dl); lw_dist2d_pt_pt(A1, B3, dl); lw_dist2d_pt_pt(A2, B1, dl); lw_dist2d_pt_pt(A2, B3, dl); return LW_TRUE; } return LW_FALSE; } int lw_dist2d_pt_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, DISTPTS *dl) { double radius_A, d; POINT2D C; /* center of circle defined by arc A */ POINT2D X; /* point circle(A) where line from C to P crosses */ if (dl->mode < 0) lwerror("lw_dist2d_pt_arc does not support maxdistance mode"); /* What if the arc is a point? */ if (lw_arc_is_pt(A1, A2, A3)) return lw_dist2d_pt_pt(P, A1, dl); /* Calculate centers and radii of circles. */ radius_A = lw_arc_center(A1, A2, A3, &C); /* This "arc" is actually a line (A2 is colinear with A1,A3) */ if (radius_A < 0.0) return lw_dist2d_pt_seg(P, A1, A3, dl); /* Distance from point to center */ d = distance2d_pt_pt(&C, P); /* P is the center of the circle */ if (FP_EQUALS(d, 0.0)) { dl->distance = radius_A; dl->p1 = *A1; dl->p2 = *P; return LW_TRUE; } /* X is the point on the circle where the line from P to C crosses */ X.x = C.x + (P->x - C.x) * radius_A / d; X.y = C.y + (P->y - C.y) * radius_A / d; /* Is crossing point inside the arc? Or arc is actually circle? */ if (p2d_same(A1, A3) || lw_pt_in_arc(&X, A1, A2, A3)) { lw_dist2d_pt_pt(P, &X, dl); } else { /* Distance is the minimum of the distances to the arc end points */ lw_dist2d_pt_pt(A1, P, dl); lw_dist2d_pt_pt(A3, P, dl); } return LW_TRUE; } /* Auxiliary function to calculate the distance between 2 concentric arcs*/ int lw_dist2d_arc_arc_concentric(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, double radius_A, const POINT2D *B1, const POINT2D *B2, const POINT2D *B3, double radius_B, const POINT2D *CENTER, DISTPTS *dl); int lw_dist2d_arc_arc(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *B1, const POINT2D *B2, const POINT2D *B3, DISTPTS *dl) { POINT2D CA, CB; /* Center points of arcs A and B */ double radius_A, radius_B, d; /* Radii of arcs A and B */ POINT2D D; /* Mid-point between the centers CA and CB */ int pt_in_arc_A, pt_in_arc_B; /* Test whether potential intersection point is within the arc */ if (dl->mode != DIST_MIN) lwerror("lw_dist2d_arc_arc only supports mindistance"); /* TODO: Handle case where arc is closed circle (A1 = A3) */ /* What if one or both of our "arcs" is actually a point? */ if (lw_arc_is_pt(B1, B2, B3) && lw_arc_is_pt(A1, A2, A3)) return lw_dist2d_pt_pt(B1, A1, dl); else if (lw_arc_is_pt(B1, B2, B3)) return lw_dist2d_pt_arc(B1, A1, A2, A3, dl); else if (lw_arc_is_pt(A1, A2, A3)) return lw_dist2d_pt_arc(A1, B1, B2, B3, dl); /* Calculate centers and radii of circles. */ radius_A = lw_arc_center(A1, A2, A3, &CA); radius_B = lw_arc_center(B1, B2, B3, &CB); /* Two co-linear arcs?!? That's two segments. */ if (radius_A < 0 && radius_B < 0) return lw_dist2d_seg_seg(A1, A3, B1, B3, dl); /* A is co-linear, delegate to lw_dist_seg_arc here. */ if (radius_A < 0) return lw_dist2d_seg_arc(A1, A3, B1, B2, B3, dl); /* B is co-linear, delegate to lw_dist_seg_arc here. */ if (radius_B < 0) return lw_dist2d_seg_arc(B1, B3, A1, A2, A3, dl); /* Center-center distance */ d = distance2d_pt_pt(&CA, &CB); /* Concentric arcs */ if (FP_EQUALS(d, 0.0)) return lw_dist2d_arc_arc_concentric(A1, A2, A3, radius_A, B1, B2, B3, radius_B, &CA, dl); /* Make sure that arc "A" has the bigger radius */ if (radius_B > radius_A) { const POINT2D *tmp; POINT2D TP; /* Temporary point P */ double td; tmp = B1; B1 = A1; A1 = tmp; tmp = B2; B2 = A2; A2 = tmp; tmp = B3; B3 = A3; A3 = tmp; TP = CB; CB = CA; CA = TP; td = radius_B; radius_B = radius_A; radius_A = td; } /* Circles touch at a point. Is that point within the arcs? */ if (d == (radius_A + radius_B)) { D.x = CA.x + (CB.x - CA.x) * radius_A / d; D.y = CA.y + (CB.y - CA.y) * radius_A / d; pt_in_arc_A = lw_pt_in_arc(&D, A1, A2, A3); pt_in_arc_B = lw_pt_in_arc(&D, B1, B2, B3); /* Arcs do touch at D, return it */ if (pt_in_arc_A && pt_in_arc_B) { dl->distance = 0.0; dl->p1 = D; dl->p2 = D; return LW_TRUE; } } /* Disjoint or contained circles don't intersect. Closest point may be on */ /* the line joining CA to CB. */ else if (d > (radius_A + radius_B) /* Disjoint */ || d < (radius_A - radius_B) /* Contained */) { POINT2D XA, XB; /* Points where the line from CA to CB cross their circle bounds */ /* Calculate hypothetical nearest points, the places on the */ /* two circles where the center-center line crosses. If both */ /* arcs contain their hypothetical points, that's the crossing distance */ XA.x = CA.x + (CB.x - CA.x) * radius_A / d; XA.y = CA.y + (CB.y - CA.y) * radius_A / d; XB.x = CB.x + (CA.x - CB.x) * radius_B / d; XB.y = CB.y + (CA.y - CB.y) * radius_B / d; pt_in_arc_A = lw_pt_in_arc(&XA, A1, A2, A3); pt_in_arc_B = lw_pt_in_arc(&XB, B1, B2, B3); /* If the nearest points are both within the arcs, that's our answer */ /* the shortest distance is at the nearest points */ if (pt_in_arc_A && pt_in_arc_B) { return lw_dist2d_pt_pt(&XA, &XB, dl); } } /* Circles cross at two points, are either of those points in both arcs? */ /* http://paulbourke.net/geometry/2circle/ */ else if (d < (radius_A + radius_B)) { POINT2D E, F; /* Points where circle(A) and circle(B) cross */ /* Distance from CA to D */ double a = (radius_A * radius_A - radius_B * radius_B + d * d) / (2 * d); /* Distance from D to E or F */ double h = sqrt(radius_A * radius_A - a * a); /* Location of D */ D.x = CA.x + (CB.x - CA.x) * a / d; D.y = CA.y + (CB.y - CA.y) * a / d; /* Start from D and project h units perpendicular to CA-D to get E */ E.x = D.x + (D.y - CA.y) * h / a; E.y = D.y + (D.x - CA.x) * h / a; /* Crossing point E contained in arcs? */ pt_in_arc_A = lw_pt_in_arc(&E, A1, A2, A3); pt_in_arc_B = lw_pt_in_arc(&E, B1, B2, B3); if (pt_in_arc_A && pt_in_arc_B) { dl->p1 = dl->p2 = E; dl->distance = 0.0; return LW_TRUE; } /* Start from D and project h units perpendicular to CA-D to get F */ F.x = D.x - (D.y - CA.y) * h / a; F.y = D.y - (D.x - CA.x) * h / a; /* Crossing point F contained in arcs? */ pt_in_arc_A = lw_pt_in_arc(&F, A1, A2, A3); pt_in_arc_B = lw_pt_in_arc(&F, B1, B2, B3); if (pt_in_arc_A && pt_in_arc_B) { dl->p1 = dl->p2 = F; dl->distance = 0.0; return LW_TRUE; } } else { lwerror("lw_dist2d_arc_arc: arcs neither touch, intersect nor are disjoint! INCONCEIVABLE!"); return LW_FALSE; } /* Closest point is in the arc A, but not in the arc B, so */ /* one of the B end points must be the closest. */ if (pt_in_arc_A && !pt_in_arc_B) { lw_dist2d_pt_arc(B1, A1, A2, A3, dl); lw_dist2d_pt_arc(B3, A1, A2, A3, dl); return LW_TRUE; } /* Closest point is in the arc B, but not in the arc A, so */ /* one of the A end points must be the closest. */ else if (pt_in_arc_B && !pt_in_arc_A) { lw_dist2d_pt_arc(A1, B1, B2, B3, dl); lw_dist2d_pt_arc(A3, B1, B2, B3, dl); return LW_TRUE; } /* Finally, one of the end-point to end-point combos is the closest. */ else { lw_dist2d_pt_pt(A1, B1, dl); lw_dist2d_pt_pt(A1, B3, dl); lw_dist2d_pt_pt(A3, B1, dl); lw_dist2d_pt_pt(A3, B3, dl); return LW_TRUE; } return LW_TRUE; } int lw_dist2d_arc_arc_concentric(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, double radius_A, const POINT2D *B1, const POINT2D *B2, const POINT2D *B3, double radius_B, const POINT2D *CENTER, DISTPTS *dl) { int seg_size; double dist_sqr, shortest_sqr; const POINT2D *P1; const POINT2D *P2; POINT2D proj; if (radius_A == radius_B) { /* Check if B1 or B3 are in the same side as A2 in the A1-A3 arc */ seg_size = lw_segment_side(A1, A3, A2); if (seg_size == lw_segment_side(A1, A3, B1)) { dl->p1 = *B1; dl->p2 = *B1; dl->distance = 0; return LW_TRUE; } if (seg_size == lw_segment_side(A1, A3, B3)) { dl->p1 = *B3; dl->p2 = *B3; dl->distance = 0; return LW_TRUE; } /* Check if A1 or A3 are in the same side as B2 in the B1-B3 arc */ seg_size = lw_segment_side(B1, B3, B2); if (seg_size == lw_segment_side(B1, B3, A1)) { dl->p1 = *A1; dl->p2 = *A1; dl->distance = 0; return LW_TRUE; } if (seg_size == lw_segment_side(B1, B3, A3)) { dl->p1 = *A3; dl->p2 = *A3; dl->distance = 0; return LW_TRUE; } } else { /* Check if any projection of B ends are in A*/ seg_size = lw_segment_side(A1, A3, A2); /* B1 */ proj.x = CENTER->x + (B1->x - CENTER->x) * radius_A / radius_B; proj.y = CENTER->y + (B1->y - CENTER->y) * radius_A / radius_B; if (seg_size == lw_segment_side(A1, A3, &proj)) { dl->p1 = proj; dl->p2 = *B1; dl->distance = fabs(radius_A - radius_B); return LW_TRUE; } /* B3 */ proj.x = CENTER->x + (B3->x - CENTER->x) * radius_A / radius_B; proj.y = CENTER->y + (B3->y - CENTER->y) * radius_A / radius_B; if (seg_size == lw_segment_side(A1, A3, &proj)) { dl->p1 = proj; dl->p2 = *B3; dl->distance = fabs(radius_A - radius_B); return LW_TRUE; } /* Now check projections of A in B */ seg_size = lw_segment_side(B1, B3, B2); /* A1 */ proj.x = CENTER->x + (A1->x - CENTER->x) * radius_B / radius_A; proj.y = CENTER->y + (A1->y - CENTER->y) * radius_B / radius_A; if (seg_size == lw_segment_side(B1, B3, &proj)) { dl->p1 = proj; dl->p2 = *A1; dl->distance = fabs(radius_A - radius_B); return LW_TRUE; } /* A3 */ proj.x = CENTER->x + (A3->x - CENTER->x) * radius_B / radius_A; proj.y = CENTER->y + (A3->y - CENTER->y) * radius_B / radius_A; if (seg_size == lw_segment_side(B1, B3, &proj)) { dl->p1 = proj; dl->p2 = *A3; dl->distance = fabs(radius_A - radius_B); return LW_TRUE; } } /* Check the shortest between the distances of the 4 ends */ shortest_sqr = dist_sqr = distance2d_sqr_pt_pt(A1, B1); P1 = A1; P2 = B1; dist_sqr = distance2d_sqr_pt_pt(A1, B3); if (dist_sqr < shortest_sqr) { shortest_sqr = dist_sqr; P1 = A1; P2 = B3; } dist_sqr = distance2d_sqr_pt_pt(A3, B1); if (dist_sqr < shortest_sqr) { shortest_sqr = dist_sqr; P1 = A3; P2 = B1; } dist_sqr = distance2d_sqr_pt_pt(A3, B3); if (dist_sqr < shortest_sqr) { shortest_sqr = dist_sqr; P1 = A3; P2 = B3; } dl->p1 = *P1; dl->p2 = *P2; dl->distance = sqrt(shortest_sqr); return LW_TRUE; } /** Finds the shortest distance between two segments. This function is changed so it is not doing any comparison of distance but just sending every possible combination further to lw_dist2d_pt_seg */ int lw_dist2d_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl) { double s_top, s_bot, s; double r_top, r_bot, r; /*A and B are the same point */ if ((A->x == B->x) && (A->y == B->y)) { return lw_dist2d_pt_seg(A, C, D, dl); } /*U and V are the same point */ if ((C->x == D->x) && (C->y == D->y)) { dl->twisted = ((dl->twisted) * (-1)); return lw_dist2d_pt_seg(D, A, B, dl); } /* AB and CD are line segments */ /* from comp.graphics.algo Solving the above for r and s yields (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) r = ----------------------------- (eqn 1) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- (eqn 2) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) Let P be the position vector of the intersection point, then P=A+r(B-A) or Px=Ax+r(Bx-Ax) Py=Ay+r(By-Ay) By examining the values of r & s, you can also determine some other limiting conditions: If 0<=r<=1 & 0<=s<=1, intersection exists r<0 or r>1 or s<0 or s>1 line segments do not intersect If the denominator in eqn 1 is zero, AB & CD are parallel If the numerator in eqn 1 is also zero, AB & CD are collinear. */ r_top = (A->y - C->y) * (D->x - C->x) - (A->x - C->x) * (D->y - C->y); r_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x); s_top = (A->y - C->y) * (B->x - A->x) - (A->x - C->x) * (B->y - A->y); s_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x); if ((r_bot == 0) || (s_bot == 0)) { if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl))) { /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/ dl->twisted *= -1; return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl))); } else return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/ } s = s_top / s_bot; r = r_top / r_bot; if (((r < 0) || (r > 1) || (s < 0) || (s > 1)) || (dl->mode == DIST_MAX)) { if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl))) { /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/ dl->twisted *= -1; return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl))); } else return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/ } else { /* If there is intersection we identify the intersection point and return it but only if we are looking * for mindistance */ if (dl->mode == DIST_MIN) { POINT2D theP; if (((A->x == C->x) && (A->y == C->y)) || ((A->x == D->x) && (A->y == D->y))) { theP.x = A->x; theP.y = A->y; } else if (((B->x == C->x) && (B->y == C->y)) || ((B->x == D->x) && (B->y == D->y))) { theP.x = B->x; theP.y = B->y; } else { theP.x = A->x + r * (B->x - A->x); theP.y = A->y + r * (B->y - A->y); } dl->distance = 0.0; dl->p1 = theP; dl->p2 = theP; } return LW_TRUE; } } /*------------------------------------------------------------------------------------------------------------ End of Brute force functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ New faster distance calculations --------------------------------------------------------------------------------------------------------------*/ /** The new faster calculation comparing pointarray to another pointarray the arrays can come from both polygons and linestrings. The naming is not good but comes from that it compares a chosen selection of the points not all of them */ int lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl, GBOX *box1, GBOX *box2) { /*here we define two lists to hold our calculated "z"-values and the order number in the geometry*/ double k, thevalue; float deltaX, deltaY, c1m, c2m; POINT2D c1, c2; const POINT2D *theP; float min1X, max1X, max1Y, min1Y, min2X, max2X, max2Y, min2Y; int t; int n1 = l1->npoints; int n2 = l2->npoints; LISTSTRUCT *list1, *list2; list1 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n1); list2 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n2); LWDEBUG(2, "lw_dist2d_fast_ptarray_ptarray is called"); max1X = box1->xmax; min1X = box1->xmin; max1Y = box1->ymax; min1Y = box1->ymin; max2X = box2->xmax; min2X = box2->xmin; max2Y = box2->ymax; min2Y = box2->ymin; /*we want the center of the bboxes, and calculate the slope between the centerpoints*/ c1.x = min1X + (max1X - min1X) / 2; c1.y = min1Y + (max1Y - min1Y) / 2; c2.x = min2X + (max2X - min2X) / 2; c2.y = min2Y + (max2Y - min2Y) / 2; deltaX = (c2.x - c1.x); deltaY = (c2.y - c1.y); /*Here we calculate where the line perpendicular to the center-center line crosses the axes for each vertex if the center-center line is vertical the perpendicular line will be horizontal and we find it's crossing the Y-axes with z = y-kx */ if ((deltaX * deltaX) < (deltaY * deltaY)) /*North or South*/ { k = -deltaX / deltaY; for (t = 0; t < n1; t++) /*for each segment in L1 */ { theP = getPoint2d_cp(l1, t); thevalue = theP->y - (k * theP->x); list1[t].themeasure = thevalue; list1[t].pnr = t; } for (t = 0; t < n2; t++) /*for each segment in L2*/ { theP = getPoint2d_cp(l2, t); thevalue = theP->y - (k * theP->x); list2[t].themeasure = thevalue; list2[t].pnr = t; } c1m = c1.y - (k * c1.x); c2m = c2.y - (k * c2.x); } /*if the center-center line is horizontal the perpendicular line will be vertical. To eliminate problems with dividing by zero we are here mirroring the coordinate-system and we find it's crossing the X-axes with z = x-(1/k)y */ else /*West or East*/ { k = -deltaY / deltaX; for (t = 0; t < n1; t++) /*for each segment in L1 */ { theP = getPoint2d_cp(l1, t); thevalue = theP->x - (k * theP->y); list1[t].themeasure = thevalue; list1[t].pnr = t; /* lwnotice("l1 %d, measure=%f",t,thevalue ); */ } for (t = 0; t < n2; t++) /*for each segment in L2*/ { theP = getPoint2d_cp(l2, t); thevalue = theP->x - (k * theP->y); list2[t].themeasure = thevalue; list2[t].pnr = t; /* lwnotice("l2 %d, measure=%f",t,thevalue ); */ } c1m = c1.x - (k * c1.y); c2m = c2.x - (k * c2.y); } /*we sort our lists by the calculated values*/ qsort(list1, n1, sizeof(LISTSTRUCT), struct_cmp_by_measure); qsort(list2, n2, sizeof(LISTSTRUCT), struct_cmp_by_measure); if (c1m < c2m) { if (!lw_dist2d_pre_seg_seg(l1, l2, list1, list2, k, dl)) { lwfree(list1); lwfree(list2); return LW_FALSE; } } else { dl->twisted = ((dl->twisted) * (-1)); if (!lw_dist2d_pre_seg_seg(l2, l1, list2, list1, k, dl)) { lwfree(list1); lwfree(list2); return LW_FALSE; } } lwfree(list1); lwfree(list2); return LW_TRUE; } int struct_cmp_by_measure(const void *a, const void *b) { LISTSTRUCT *ia = (LISTSTRUCT *)a; LISTSTRUCT *ib = (LISTSTRUCT *)b; return (ia->themeasure > ib->themeasure) ? 1 : ((ia->themeasure < ib->themeasure) ? -1 : 0); } /** preparation before lw_dist2d_seg_seg. */ int lw_dist2d_pre_seg_seg(POINTARRAY *l1, POINTARRAY *l2, LISTSTRUCT *list1, LISTSTRUCT *list2, double k, DISTPTS *dl) { const POINT2D *p1, *p2, *p3, *p4, *p01, *p02; int pnr1, pnr2, pnr3, pnr4, n1, n2, i, u, r, twist; double maxmeasure; n1 = l1->npoints; n2 = l2->npoints; LWDEBUG(2, "lw_dist2d_pre_seg_seg is called"); p1 = getPoint2d_cp(l1, list1[0].pnr); p3 = getPoint2d_cp(l2, list2[0].pnr); lw_dist2d_pt_pt(p1, p3, dl); maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k)); twist = dl->twisted; /*to keep the incoming order between iterations*/ for (i = (n1 - 1); i >= 0; --i) { /*we break this iteration when we have checked every point closer to our perpendicular "checkline" than * our shortest found distance*/ if (((list2[0].themeasure - list1[i].themeasure)) > maxmeasure) break; /*because we are not iterating in the original point order we have to check the segment before and after * every point*/ for (r = -1; r <= 1; r += 2) { pnr1 = list1[i].pnr; p1 = getPoint2d_cp(l1, pnr1); if (pnr1 + r < 0) { p01 = getPoint2d_cp(l1, (n1 - 1)); if ((p1->x == p01->x) && (p1->y == p01->y)) pnr2 = (n1 - 1); else pnr2 = pnr1; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else if (pnr1 + r > (n1 - 1)) { p01 = getPoint2d_cp(l1, 0); if ((p1->x == p01->x) && (p1->y == p01->y)) pnr2 = 0; else pnr2 = pnr1; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr2 = pnr1 + r; p2 = getPoint2d_cp(l1, pnr2); for (u = 0; u < n2; ++u) { if (((list2[u].themeasure - list1[i].themeasure)) >= maxmeasure) break; pnr3 = list2[u].pnr; p3 = getPoint2d_cp(l2, pnr3); if (pnr3 == 0) { p02 = getPoint2d_cp(l2, (n2 - 1)); if ((p3->x == p02->x) && (p3->y == p02->y)) pnr4 = (n2 - 1); else pnr4 = pnr3; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr4 = pnr3 - 1; p4 = getPoint2d_cp(l2, pnr4); dl->twisted = twist; if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl)) return LW_FALSE; if (pnr3 >= (n2 - 1)) { p02 = getPoint2d_cp(l2, 0); if ((p3->x == p02->x) && (p3->y == p02->y)) pnr4 = 0; else pnr4 = pnr3; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr4 = pnr3 + 1; p4 = getPoint2d_cp(l2, pnr4); dl->twisted = twist; /*we reset the "twist" for each iteration*/ if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl)) return LW_FALSE; /*here we "translate" the found mindistance so it can be compared to our "z"-values*/ maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k)); } } } return LW_TRUE; } /** This is the same function as lw_dist2d_seg_seg but without any calculations to determine intersection since we already know they do not intersect */ int lw_dist2d_selected_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl) { /*A and B are the same point */ if ((A->x == B->x) && (A->y == B->y)) { return lw_dist2d_pt_seg(A, C, D, dl); } /*U and V are the same point */ if ((C->x == D->x) && (C->y == D->y)) { dl->twisted *= -1; return lw_dist2d_pt_seg(D, A, B, dl); } if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl))) { /* change the order of inputted geometries and that we notice by changing sign on dl->twisted */ dl->twisted *= -1; return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl))); } else return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/ } /*------------------------------------------------------------------------------------------------------------ End of New faster distance calculations --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Functions in common for Brute force and new calculation --------------------------------------------------------------------------------------------------------------*/ /** lw_dist2d_comp from p to line A->B This one is now sending every occasion to lw_dist2d_pt_pt Before it was handling occasions where r was between 0 and 1 internally and just returning the distance without identifying the points. To get this points it was necessary to change and it also showed to be about 10%faster. */ int lw_dist2d_pt_seg(const POINT2D *p, const POINT2D *A, const POINT2D *B, DISTPTS *dl) { POINT2D c; double r; /*if start==end, then use pt distance */ if ((A->x == B->x) && (A->y == B->y)) return lw_dist2d_pt_pt(p, A, dl); /* * otherwise, we use comp.graphics.algorithms * Frequently Asked Questions method * * (1) AC dot AB * r = --------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x - A->x) * (B->x - A->x) + (p->y - A->y) * (B->y - A->y)) / ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y)); /*This is for finding the maxdistance. the maxdistance have to be between two vertexes, compared to mindistance which can be between two vertexes.*/ if (dl->mode == DIST_MAX) { if (r >= 0.5) return lw_dist2d_pt_pt(p, A, dl); else /* (r < 0.5) */ return lw_dist2d_pt_pt(p, B, dl); } if (r < 0) /*If p projected on the line is outside point A*/ return lw_dist2d_pt_pt(p, A, dl); if (r >= 1) /*If p projected on the line is outside point B or on point B*/ return lw_dist2d_pt_pt(p, B, dl); /*If the point p is on the segment this is a more robust way to find out that*/ if ((((A->y - p->y) * (B->x - A->x) == (A->x - p->x) * (B->y - A->y))) && (dl->mode == DIST_MIN)) { dl->distance = 0.0; dl->p1 = *p; dl->p2 = *p; } /*If the projection of point p on the segment is between A and B then we find that "point on segment" and send it to lw_dist2d_pt_pt*/ c.x = A->x + r * (B->x - A->x); c.y = A->y + r * (B->y - A->y); return lw_dist2d_pt_pt(p, &c, dl); } /** Compares incoming points and stores the points closest to each other or most far away from each other depending on * dl->mode (max or min) */ int lw_dist2d_pt_pt(const POINT2D *thep1, const POINT2D *thep2, DISTPTS *dl) { double hside = thep2->x - thep1->x; double vside = thep2->y - thep1->y; double dist = sqrt(hside * hside + vside * vside); /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/ if (((dl->distance - dist) * (dl->mode)) > 0) { dl->distance = dist; /* To get the points in right order. twisted is updated between 1 and (-1) every time the order is * changed earlier in the chain*/ if (dl->twisted > 0) { dl->p1 = *thep1; dl->p2 = *thep2; } else { dl->p1 = *thep2; dl->p2 = *thep1; } } return LW_TRUE; } /*------------------------------------------------------------------------------------------------------------ End of Functions in common for Brute force and new calculation --------------------------------------------------------------------------------------------------------------*/ inline double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2) { double hside = p2->x - p1->x; double vside = p2->y - p1->y; return hypot(hside, vside); } /* return distance squared, useful to avoid sqrt calculations */ double distance2d_sqr_pt_seg(const POINT2D *C, const POINT2D *A, const POINT2D *B) { /*if start==end, then use pt distance */ if ((A->x == B->x) && (A->y == B->y)) return distance2d_sqr_pt_pt(C, A); /* * otherwise, we use comp.graphics.algorithms * Frequently Asked Questions method * * (1) AC dot AB * r = --------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x - A->x); double ba_y = (B->y - A->y); double ab_length_sqr = (ba_x * ba_x + ba_y * ba_y); double ca_x = (C->x - A->x); double ca_y = (C->y - A->y); double dot_ac_ab = (ca_x * ba_x + ca_y * ba_y); if (dot_ac_ab <= 0) return distance2d_sqr_pt_pt(C, A); if (dot_ac_ab >= ab_length_sqr) return distance2d_sqr_pt_pt(C, B); /* * (2) * (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) * s = ----------------------------- * L^2 * * Then the distance from C to P = |s|*L. * */ double s_numerator = ca_x * ba_y - ca_y * ba_x; /* Distance = (s_num / ab) * (s_num / ab) * ab == s_num * s_num / ab) */ return s_numerator * s_numerator / ab_length_sqr; } /** * Compute the azimuth of segment AB in radians. * Return 0 on exception (same point), 1 otherwise. */ int azimuth_pt_pt(const POINT2D *A, const POINT2D *B, double *d) { if (A->x == B->x && A->y == B->y) return LW_FALSE; *d = fmod(2 * M_PI + M_PI / 2 - atan2(B->y - A->y, B->x - A->x), 2 * M_PI); return LW_TRUE; } lwgeom/src/liblwgeom/lwgeodetic_tree.h0000644000176200001440000000376513773172540017656 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2012-2015 Paul Ramsey * **********************************************************************/ #ifndef _LWGEODETIC_TREE_H #define _LWGEODETIC_TREE_H 1 #include "lwgeodetic.h" #define CIRC_NODE_SIZE 8 /** * Note that p1 and p2 are pointers into an independent POINTARRAY, do not free them. */ typedef struct circ_node { GEOGRAPHIC_POINT center; double radius; uint32_t num_nodes; struct circ_node** nodes; int edge_num; uint32_t geom_type; double d; POINT2D pt_outside; POINT2D* p1; POINT2D* p2; } CIRC_NODE; void circ_tree_print(const CIRC_NODE* node, int depth); CIRC_NODE* circ_tree_new(const POINTARRAY* pa); void circ_tree_free(CIRC_NODE* node); int circ_tree_contains_point(const CIRC_NODE* node, const POINT2D* pt, const POINT2D* pt_outside, int level, int* on_boundary); double circ_tree_distance_tree(const CIRC_NODE* n1, const CIRC_NODE* n2, const SPHEROID *spheroid, double threshold); CIRC_NODE* lwgeom_calculate_circ_tree(const LWGEOM* lwgeom); int circ_tree_get_point(const CIRC_NODE* node, POINT2D* pt); int circ_tree_get_point_outside(const CIRC_NODE* node, POINT2D* pt); #endif /* _LWGEODETIC_TREE_H */ lwgeom/src/liblwgeom/lwgeom_api.c0000644000176200001440000003215214332732704016613 0ustar liggesusers/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * Copyright 2017 Darafei Praliaskouski * **********************************************************************/ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include #include #include "../postgis_svn_revision.h" const char * lwgeom_version(void) { static char *ptr = NULL; static char buf[256]; if ( ! ptr ) { ptr = buf; snprintf(ptr, 256, LIBLWGEOM_VERSION" r%d", POSTGIS_SVN_REVISION); } return ptr; } inline float next_float_down(double d) { float result; if (d > (double)FLT_MAX) return FLT_MAX; if (d <= (double)-FLT_MAX) return -FLT_MAX; result = d; if ( ((double)result) <=d ) return result; return nextafterf(result, -1*FLT_MAX); } /* * Returns the float that's very close to the input, but >=. * handles the funny differences in float4 and float8 reps. */ inline float next_float_up(double d) { float result; if (d >= (double)FLT_MAX) return FLT_MAX; if (d < (double)-FLT_MAX) return -FLT_MAX; result = d; if ( ((double)result) >=d ) return result; return nextafterf(result, FLT_MAX); } /************************************************************************ * POINTARRAY support functions * * TODO: should be moved to ptarray.c probably * ************************************************************************/ /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: point is a real POINT3D *not* a pointer */ POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n) { POINT4D result; getPoint4d_p(pa, n, &result); return result; } /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: this will modify the point4d pointed to by 'point'. * * @return 0 on error, 1 on success */ int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *op) { uint8_t *ptr; int zmflag; if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } if ( n>=pa->npoints ) { lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } LWDEBUG(4, "getPoint4d_p called."); /* Get a pointer to nth point offset and zmflag */ ptr=getPoint_internal(pa, n); zmflag=FLAGS_GET_ZM(pa->flags); LWDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag); switch (zmflag) { case 0: /* 2d */ memcpy(op, ptr, sizeof(POINT2D)); op->m=NO_M_VALUE; op->z=NO_Z_VALUE; break; case 3: /* ZM */ memcpy(op, ptr, sizeof(POINT4D)); break; case 2: /* Z */ memcpy(op, ptr, sizeof(POINT3DZ)); op->m=NO_M_VALUE; break; case 1: /* M */ memcpy(op, ptr, sizeof(POINT3DM)); op->m=op->z; /* we use Z as temporary storage */ op->z=NO_Z_VALUE; break; default: lwerror("Unknown ZM flag ??"); return 0; } return 1; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * NOTE: point is a real POINT3DZ *not* a pointer */ POINT3DZ getPoint3dz(const POINTARRAY *pa, uint32_t n) { POINT3DZ result; getPoint3dz_p(pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: point is a real POINT3DZ *not* a pointer */ POINT3DM getPoint3dm(const POINTARRAY *pa, uint32_t n) { POINT3DM result; getPoint3dm_p(pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: this will modify the point3dz pointed to by 'point'. */ int getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *op) { uint8_t *ptr; if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } //assert(n < pa->npoints); --causes point emtpy/point empty to crash if ( n>=pa->npoints ) { lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } LWDEBUGF(2, "getPoint3dz_p called on array of %d-dimensions / %u pts", FLAGS_NDIMS(pa->flags), pa->npoints); /* Get a pointer to nth point offset */ ptr=getPoint_internal(pa, n); /* * if input POINTARRAY has the Z, it is always * at third position so make a single copy */ if ( FLAGS_GET_Z(pa->flags) ) { memcpy(op, ptr, sizeof(POINT3DZ)); } /* * Otherwise copy the 2d part and initialize * Z to NO_Z_VALUE */ else { memcpy(op, ptr, sizeof(POINT2D)); op->z=NO_Z_VALUE; } return 1; } /* * Copy a point from the point array into the parameter point * will set point's m=NO_Z_VALUE if pa has no M * * NOTE: this will modify the point3dm pointed to by 'point'. */ int getPoint3dm_p(const POINTARRAY *pa, uint32_t n, POINT3DM *op) { uint8_t *ptr; int zmflag; if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } if ( n>=pa->npoints ) { lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } LWDEBUGF(2, "getPoint3dm_p(%d) called on array of %d-dimensions / %u pts", n, FLAGS_NDIMS(pa->flags), pa->npoints); /* Get a pointer to nth point offset and zmflag */ ptr=getPoint_internal(pa, n); zmflag=FLAGS_GET_ZM(pa->flags); /* * if input POINTARRAY has the M and NO Z, * we can issue a single memcpy */ if ( zmflag == 1 ) { memcpy(op, ptr, sizeof(POINT3DM)); return 1; } /* * Otherwise copy the 2d part and * initialize M to NO_M_VALUE */ memcpy(op, ptr, sizeof(POINT2D)); /* * Then, if input has Z skip it and * copy next double, otherwise initialize * M to NO_M_VALUE */ if ( zmflag == 3 ) { ptr+=sizeof(POINT3DZ); memcpy(&(op->m), ptr, sizeof(double)); } else { op->m=NO_M_VALUE; } return 1; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: point is a real POINT2D *not* a pointer */ POINT2D getPoint2d(const POINTARRAY *pa, uint32_t n) { const POINT2D *result; result = getPoint2d_cp(pa, n); return *result; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: this will modify the point2d pointed to by 'point'. */ int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point) { if ( ! pa ) { lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); return 0; } if ( n>=pa->npoints ) { lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); return 0; } /* this does x,y */ memcpy(point, getPoint_internal(pa, n), sizeof(POINT2D)); return 1; } /* * set point N to the given value * NOTE that the pointarray can be of any * dimension, the appropriate ordinate values * will be extracted from it * */ void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d) { uint8_t *ptr; assert(n < pa->npoints); ptr = getPoint_internal(pa, n); switch ( FLAGS_GET_ZM(pa->flags) ) { case 3: memcpy(ptr, p4d, sizeof(POINT4D)); break; case 2: memcpy(ptr, p4d, sizeof(POINT3DZ)); break; case 1: memcpy(ptr, p4d, sizeof(POINT2D)); ptr+=sizeof(POINT2D); memcpy(ptr, &(p4d->m), sizeof(double)); break; case 0: memcpy(ptr, p4d, sizeof(POINT2D)); break; } } void ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to) { int ndims = FLAGS_NDIMS(pa->flags); switch (ndims) { case 2: { POINT2D *p_from = (POINT2D*)(getPoint_internal(pa, from)); POINT2D *p_to = (POINT2D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } case 3: { POINT3D *p_from = (POINT3D*)(getPoint_internal(pa, from)); POINT3D *p_to = (POINT3D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } case 4: { POINT4D *p_from = (POINT4D*)(getPoint_internal(pa, from)); POINT4D *p_to = (POINT4D*)(getPoint_internal(pa, to)); *p_to = *p_from; return; } default: { lwerror("%s: unsupported number of dimensions - %d", __func__, ndims); return; } } return; } /************************************************ * debugging routines ************************************************/ void printBOX3D(BOX3D *box) { lwnotice("BOX3D: %g %g, %g %g", box->xmin, box->ymin, box->xmax, box->ymax); } void printPA(POINTARRAY *pa) { uint32_t t; POINT4D pt; char *mflag; if ( FLAGS_GET_M(pa->flags) ) mflag = "M"; else mflag = ""; lwnotice(" POINTARRAY%s{", mflag); lwnotice(" ndims=%i, ptsize=%i", FLAGS_NDIMS(pa->flags), ptarray_point_size(pa)); lwnotice(" npoints = %i", pa->npoints); if (!pa) { lwnotice(" PTARRAY is null pointer!"); } else { for (t = 0; t < pa->npoints; t++) { getPoint4d_p(pa, t, &pt); if (FLAGS_NDIMS(pa->flags) == 2) lwnotice(" %i : %lf,%lf", t, pt.x, pt.y); if (FLAGS_NDIMS(pa->flags) == 3) lwnotice(" %i : %lf,%lf,%lf", t, pt.x, pt.y, pt.z); if (FLAGS_NDIMS(pa->flags) == 4) lwnotice(" %i : %lf,%lf,%lf,%lf", t, pt.x, pt.y, pt.z, pt.m); } } lwnotice(" }"); } /** * Given a string with at least 2 chars in it, convert them to * a byte value. No error checking done! */ uint8_t parse_hex(char *str) { /* do this a little brute force to make it faster */ uint8_t result_high = 0; uint8_t result_low = 0; switch (str[0]) { case '0' : result_high = 0; break; case '1' : result_high = 1; break; case '2' : result_high = 2; break; case '3' : result_high = 3; break; case '4' : result_high = 4; break; case '5' : result_high = 5; break; case '6' : result_high = 6; break; case '7' : result_high = 7; break; case '8' : result_high = 8; break; case '9' : result_high = 9; break; case 'A' : case 'a' : result_high = 10; break; case 'B' : case 'b' : result_high = 11; break; case 'C' : case 'c' : result_high = 12; break; case 'D' : case 'd' : result_high = 13; break; case 'E' : case 'e' : result_high = 14; break; case 'F' : case 'f' : result_high = 15; break; } switch (str[1]) { case '0' : result_low = 0; break; case '1' : result_low = 1; break; case '2' : result_low = 2; break; case '3' : result_low = 3; break; case '4' : result_low = 4; break; case '5' : result_low = 5; break; case '6' : result_low = 6; break; case '7' : result_low = 7; break; case '8' : result_low = 8; break; case '9' : result_low = 9; break; case 'A' : case 'a' : result_low = 10; break; case 'B' : case 'b' : result_low = 11; break; case 'C' : case 'c' : result_low = 12; break; case 'D' : case 'd' : result_low = 13; break; case 'E' : case 'e' : result_low = 14; break; case 'F' : case 'f' : result_low = 15; break; } return (uint8_t) ((result_high<<4) + result_low); } /** * Given one byte, populate result with two byte representing * the hex number. * * Ie. deparse_hex( 255, mystr) * -> mystr[0] = 'F' and mystr[1] = 'F' * * No error checking done */ void deparse_hex(uint8_t str, char *result) { int input_high; int input_low; static char outchr[]= { "0123456789ABCDEF" }; input_high = (str>>4); input_low = (str & 0x0F); result[0] = outchr[input_high]; result[1] = outchr[input_low]; } /** * Find interpolation point I * between point A and point B * so that the len(AI) == len(AB)*F * and I falls on AB segment. * * Example: * * F=0.5 : A----I----B * F=1 : A---------B==I * F=0 : A==I---------B * F=.2 : A-I-------B */ void interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F) { #if PARANOIA_LEVEL > 0 if (F < 0 || F > 1) lwerror("interpolate_point4d: invalid F (%g)", F); #endif I->x=A->x+((B->x-A->x)*F); I->y=A->y+((B->y-A->y)*F); I->z=A->z+((B->z-A->z)*F); I->m=A->m+((B->m-A->m)*F); } int _lwgeom_interrupt_requested = 0; void lwgeom_request_interrupt(void) { _lwgeom_interrupt_requested = 1; } void lwgeom_cancel_interrupt(void) { _lwgeom_interrupt_requested = 0; } lwinterrupt_callback *_lwgeom_interrupt_callback = 0; lwinterrupt_callback * lwgeom_register_interrupt_callback(lwinterrupt_callback *cb) { lwinterrupt_callback *old = _lwgeom_interrupt_callback; _lwgeom_interrupt_callback = cb; return old; } lwgeom/src/lwgeom.cpp0000644000176200001440000002204614076540103014335 0ustar liggesusers// [[Rcpp::depends(sf)]] #include #include #include extern "C" { #include #ifdef HAVE_LIBLWGEOM_INTERNAL_H # include #else /* hard copy from liblwgeom_internal.h: */ # ifndef NO_GRID_IN_PLACE /* typedef struct gridspec_t { double ipx; double ipy; double ipz; double ipm; double xsize; double ysize; double zsize; double msize; } gridspec; */ void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid); # endif #endif } #include "lwgeom.h" using namespace Rcpp; // for _ to work // [[Rcpp::export]] Rcpp::CharacterVector CPL_lwgeom_version(bool b = false) { return lwgeom_version(); } // in std::vector lwgeom_from_sfc(Rcpp::List sfc) { std::vector lwgeom_v(sfc.size()); // return Rcpp::List wkblst = sf::CPL_write_wkb(sfc, true); // true: write EWKB, puts EPSG inside the wkb for (int i = 0; i < wkblst.size(); i++) { Rcpp::RawVector rv = wkblst[i]; const uint8_t *wkb = &(rv[0]); lwgeom_v[i] = lwgeom_from_wkb(wkb, rv.size(), LW_PARSER_CHECK_MINPOINTS & LW_PARSER_CHECK_ODD & LW_PARSER_CHECK_CLOSURE); } return lwgeom_v; } // out Rcpp::List sfc_from_lwgeom(std::vector lwgeom_v) { Rcpp::List wkblst(lwgeom_v.size()); for (int i = 0; i < wkblst.size(); i++) { size_t size; const uint8_t *wkb = lwgeom_to_wkb(lwgeom_v[i], WKB_EXTENDED, &size); lwgeom_free(lwgeom_v[i]); Rcpp::RawVector raw(size); memcpy(&(raw[0]), wkb, size); lwfree((void *) wkb); wkblst[i] = raw; } return sf::CPL_read_wkb(wkblst, true, false); } // [[Rcpp::export]] Rcpp::List CPL_sfc_from_twkb(Rcpp::List twkb) { std::vector lw(twkb.size()); for (size_t i = 0; i < lw.size(); i++) { Rcpp::RawVector raw = twkb[i]; lw[i] = lwgeom_from_twkb(&(raw[0]), raw.size(), LW_PARSER_CHECK_ALL); } return sfc_from_lwgeom(lw); } // [[Rcpp::export]] Rcpp::List CPL_make_valid(Rcpp::List sfc) { std::vector lwgeom_v = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_v.size(); i++) { // do the trick: LWGEOM *lwg_ret = lwgeom_make_valid(lwgeom_v[i]); lwgeom_free(lwgeom_v[i]); lwgeom_v[i] = lwg_ret; } return sfc_from_lwgeom(lwgeom_v); } // [[Rcpp::export]] Rcpp::List CPL_split(Rcpp::List sfc, Rcpp::List blade) { std::vector lwgeom_in = lwgeom_from_sfc(sfc); std::vector lwgeom_blade = lwgeom_from_sfc(blade); for (size_t i = 0; i < lwgeom_in.size(); i++) { LWGEOM *lwg_ret = lwgeom_split(lwgeom_in[i], lwgeom_blade[0]); lwgeom_free(lwgeom_in[i]); lwgeom_in[i] = lwg_ret; } sfc_from_lwgeom(lwgeom_blade); // free return sfc_from_lwgeom(lwgeom_in); } // [[Rcpp::export]] Rcpp::List CPL_wrap_x(Rcpp::List sfc, Rcpp::NumericVector wrap, Rcpp::NumericVector move) { if (wrap.size() != 1) { Rcpp::stop("Must supply a scalar value for `wrap`"); } if (move.size() != 1) { Rcpp::stop("Must supply a scalar value for `move`"); } std::vector lwgeom_v = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_v.size(); i++) { LWGEOM *lwg_ret = lwgeom_wrapx(lwgeom_v[i], wrap[0], move[0]); lwgeom_free(lwgeom_v[i]); lwgeom_v[i] = lwg_ret; } return sfc_from_lwgeom(lwgeom_v); } // [[Rcpp::export]] Rcpp::CharacterVector CPL_geohash(Rcpp::List sfc, int prec) { Rcpp::CharacterVector chr(sfc.size()); // return std::vector lwgeom_v = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_v.size(); i++) { char *c = lwgeom_geohash(lwgeom_v[i], prec); chr(i) = c; // copy lwfree(c); lwgeom_free(lwgeom_v[i]); } return chr; } // [[Rcpp::export]] Rcpp::List CPL_lwgeom_transform(Rcpp::List sfc, Rcpp::CharacterVector p4s) { if (p4s.size() != 2) Rcpp::stop("st_lwgeom_transform: p4s needs to be a length 2 character vector\n"); // #nocov std::vector lwgeom_v = lwgeom_from_sfc(sfc); LWPROJ *pj; #ifdef USE_PROJ_H proj_context_use_proj4_init_rules(PJ_DEFAULT_CTX, 1); PJ *P = proj_create_crs_to_crs(PJ_DEFAULT_CTX, p4s[0], p4s[1], NULL); if (P == NULL) Rcpp::stop("st_lwgeom_transform: one of the proj strings is invalid\n"); // #nocov pj = lwproj_from_PJ(P, 0); #else pj = (LWPROJ *) malloc(sizeof(LWPROJ)); projPJ src = projpj_from_string(p4s[0]); if (src == NULL) Rcpp::stop("st_lwgeom_transform: wrong source proj4string\n"); // #nocov pj->pj_from = src; projPJ target = projpj_from_string(p4s[1]); if (target == NULL) Rcpp::stop("st_lwgeom_transform: wrong target proj4string\n"); // #nocov pj->pj_to = target; #endif for (size_t i = 0; i < lwgeom_v.size(); i++) { // in-place transformation w/o GDAL: if (lwgeom_transform(lwgeom_v[i], pj) != LW_SUCCESS) { Rcpp::Rcout << "Failed on geometry " << i + 1 << std::endl; // #nocov Rcpp::stop("st_lwgeom_transform failed\n"); // #nocov } } #ifdef USE_PROJ_H proj_destroy(P); #else pj_free(src); pj_free(target); free(pj); #endif Rcpp::List ret = sfc_from_lwgeom(lwgeom_v); // frees lwgeom_v // FIXME: /* Rcpp::List crs = Rcpp::List::create( _["epsg"] = NA_INTEGER, _["proj4string"] = CharacterVector::create(p4s[1])); crs.attr("class") = "crs"; ret.attr("crs") = crs; */ ret.attr("class") = "sfc"; return ret; } // [[Rcpp::export]] Rcpp::List CPL_minimum_bounding_circle(Rcpp::List sfc) { Rcpp::List center(sfc.size()); Rcpp::NumericVector radius(sfc.size()); std::vector lwgeom_v = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_v.size(); i++) { LWBOUNDINGCIRCLE *lwg_ret = lwgeom_calculate_mbc(lwgeom_v[i]); if (lwg_ret == NULL) Rcpp::stop("could not compute minimum bounding circle"); // #nocov center[i] = Rcpp::NumericVector::create( Rcpp::Named("x") = lwg_ret->center->x, Rcpp::Named("y") = lwg_ret->center->y ); radius[i] = lwg_ret->radius; lwgeom_free(lwgeom_v[i]); lwboundingcircle_destroy(lwg_ret); } return Rcpp::List::create( Rcpp::Named("center") = center, Rcpp::Named("radius") = radius ); } // [[Rcpp::export]] Rcpp::List CPL_subdivide(Rcpp::List sfc, int max_vertices = 256) { std::vector lwgeom_v = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_v.size(); i++) lwgeom_v[i] = lwcollection_as_lwgeom(lwgeom_subdivide(lwgeom_v[i], max_vertices)); return sfc_from_lwgeom(lwgeom_v); } // [[Rcpp::export]] Rcpp::List CPL_snap_to_grid(Rcpp::List sfc, Rcpp::NumericVector origin, Rcpp::NumericVector size) { #ifdef NO_GRID_IN_PLACE Rcpp::stop("st_snap_to_grid: not supported in this version of liblwgeom\n"); // #nocov // return sfc; #else // initialize input data std::vector lwgeom_v = lwgeom_from_sfc(sfc); // initialize grid gridspec grid; grid.ipx = origin[0]; grid.ipy = origin[1]; grid.ipz = origin[2]; grid.ipm = origin[3]; grid.xsize = size[0]; grid.ysize = size[1]; grid.zsize = size[2]; grid.msize = size[3]; // snap geometries to grid for (size_t i = 0; i < lwgeom_v.size(); i++) lwgeom_grid_in_place(lwgeom_v[i], &grid); // return snapped geometries return sfc_from_lwgeom(lwgeom_v); #endif } // [[Rcpp::export]] Rcpp::NumericVector CPL_perimeter(Rcpp::List sfc, bool do2d = false) { Rcpp::NumericVector out(sfc.length()); std::vector lwgeom_v = lwgeom_from_sfc(sfc); if (do2d) { for (size_t i = 0; i < lwgeom_v.size(); i++) out[i] = lwgeom_perimeter_2d(lwgeom_v[i]); } else { for (size_t i = 0; i < lwgeom_v.size(); i++) out[i] = lwgeom_perimeter(lwgeom_v[i]); } return out; } // [[Rcpp::export]] Rcpp::LogicalVector CPL_is_polygon_cw(Rcpp::List sfc) { std::vector lwgeom_cw = lwgeom_from_sfc(sfc); Rcpp::LogicalVector out(sfc.length()); for (size_t i = 0; i < lwgeom_cw.size(); i++) { out[i] = lwgeom_is_clockwise(lwgeom_cw[i]); lwgeom_free(lwgeom_cw[i]); } return out; } // [[Rcpp::export]] Rcpp::List CPL_force_polygon_cw(Rcpp::List sfc) { std::vector lwgeom_cw = lwgeom_from_sfc(sfc); for (size_t i = 0; i < lwgeom_cw.size(); i++) { lwgeom_force_clockwise(lwgeom_cw[i]); } return sfc_from_lwgeom(lwgeom_cw); } // [[Rcpp::export]] Rcpp::NumericMatrix CPL_startpoint(Rcpp::List sfc) { std::vector lwgeom_cw = lwgeom_from_sfc(sfc); Rcpp::NumericMatrix m(lwgeom_cw.size(), 2); POINT4D p; for (size_t i = 0; i < lwgeom_cw.size(); i++) { lwgeom_startpoint(lwgeom_cw[i], &p); m(i, 0) = p.x; m(i, 1) = p.y; } return m; // next step: get it into sf form // return sfc_from_lwgeom(lwgeom_cw); } // [[Rcpp::export]] Rcpp::NumericMatrix CPL_endpoint(Rcpp::List sfc) { std::vector lwgeom_cw = lwgeom_from_sfc(sfc); Rcpp::NumericMatrix m(lwgeom_cw.size(), 2); // no lwgeom_endpoint so reverse in-place and use startpoint POINT4D p; for (size_t i = 0; i < lwgeom_cw.size(); i++) { lwgeom_reverse_in_place(lwgeom_cw[i]); lwgeom_startpoint(lwgeom_cw[i], &p); m(i, 0) = p.x; m(i, 1) = p.y; } return m; } // [[Rcpp::export]] Rcpp::CharacterVector CPL_sfc_to_wkt(Rcpp::List sfc, Rcpp::IntegerVector precision) { std::vector lwgeom_cw = lwgeom_from_sfc(sfc); Rcpp::CharacterVector out; for (size_t i = 0; i < lwgeom_cw.size(); i++) { char *wkt = lwgeom_to_wkt(lwgeom_cw[i], WKT_EXTENDED, precision[0], NULL); out.push_back(wkt); free(wkt); } return out; } lwgeom/src/Makevars.ucrt0000644000176200001440000000632314432625416015016 0ustar liggesusersSTATLIB = liblwgeom/liblwgeomstatic.a OBJECTS_LIBLWGEOM= \ liblwgeom/varint.o \ liblwgeom/lwout_twkb.o \ liblwgeom/lwpsurface.o \ liblwgeom/lwtriangle.o \ liblwgeom/lwmpoly.o \ liblwgeom/lookup3.o \ liblwgeom/lwin_wkt.o \ liblwgeom/gserialized1.o \ liblwgeom/lwgeom.o \ liblwgeom/gserialized2.o \ liblwgeom/lwstroke.o \ liblwgeom/lwtin.o \ liblwgeom/lwin_twkb.o \ liblwgeom/lwgeom_geos_cluster.o \ liblwgeom/lwmsurface.o \ liblwgeom/lwgeom_wrapx.o \ liblwgeom/lwiterator.o \ liblwgeom/lwgeom_geos_node.o \ liblwgeom/lwout_geojson.o \ liblwgeom/lwgeom_debug.o \ liblwgeom/lwgeom_median.o \ liblwgeom/lwmval.o \ liblwgeom/lwkmeans.o \ liblwgeom/lwgeom_geos.o \ liblwgeom/lwout_kml.o \ liblwgeom/lwutil.o \ liblwgeom/lwprint.o \ liblwgeom/lwrandom.o \ liblwgeom/lwmline.o \ liblwgeom/lwgeodetic_tree.o \ liblwgeom/lwline.o \ liblwgeom/bytebuffer.o \ liblwgeom/lwgeodetic.o \ liblwgeom/measures.o \ liblwgeom/lwgeom_api.o \ liblwgeom/lwmcurve.o \ liblwgeom/lwcollection.o \ liblwgeom/gbox.o \ liblwgeom/lwspheroid.o \ liblwgeom/lwout_svg.o \ liblwgeom/lwin_encoded_polyline.o \ liblwgeom/lwout_encoded_polyline.o \ liblwgeom/lwgeom_geos_split.o \ liblwgeom/effectivearea.o \ liblwgeom/lwboundingcircle.o \ liblwgeom/lwcurvepoly.o \ liblwgeom/lwlinearreferencing.o \ liblwgeom/lwunionfind.o \ liblwgeom/lwchaikins.o \ liblwgeom/lwalgorithm.o \ liblwgeom/lwhomogenize.o \ liblwgeom/lwgeom_geos_clean.o \ liblwgeom/measures3d.o \ liblwgeom/lwout_x3d.o \ liblwgeom/lwgeom_transform.o \ liblwgeom/lwin_wkt_lex.o \ liblwgeom/lwmpoint.o \ liblwgeom/stringbuffer.o \ liblwgeom/lwcompound.o \ liblwgeom/gserialized.o \ liblwgeom/lwout_wkt.o \ liblwgeom/lwin_wkb.o \ liblwgeom/ptarray.o \ liblwgeom/lwout_wkb.o \ liblwgeom/lwpoly.o \ liblwgeom/lwpoint.o \ liblwgeom/lwout_gml.o \ liblwgeom/lwgeom_topo.o \ liblwgeom/lwcircstring.o \ liblwgeom/lwin_wkt_parse.o PKG_CPPFLAGS = -I./liblwgeom -DUSE_PROJ_H LIBSHARPYUV = $(or $(and $(wildcard $(R_TOOLS_SOFT)/lib/libsharpyuv.a),-lsharpyuv),) PKG_LIBS = \ -L./liblwgeom -llwgeomstatic -lcurl -ltiff -lgeotiff -lproj \ -fopenmp -lgdal -larmadillo -lopenblas -lgfortran -lquadmath -lpq -lpgcommon -lpgport -lodbc32 -lodbccp32 -lblosc -lkea -lhdf5_cpp -lhdf5 -lpoppler -llcms2 -lfreetype -lharfbuzz -lfreetype -llz4 -lpcre2-8 -lxml2 -lopenjp2 -lnetcdf -lmysqlclient -lspatialite -lgeos_c -lgeos -lminizip -lgeos -ljson-c -lgta -lfreexl -lexpat -lssl -lpsapi -lgif -lmfhdf -lhdf5_hl -lcrypto -lportablexdr -ldf -lhdf5 -lsz -lpng16 -lpng -lpoppler -llcms2 -lfreetype -lharfbuzz -lfreetype -llz4 -lpcre2-8 -lpcre -lcurl -lbcrypt -lrtmp -lssl -lssh2 -lidn2 -lunistring -liconv -lgcrypt -lcrypto -lgpg-error -lws2_32 -ltiff -llzma -ljpeg -lz -lcfitsio -lzstd -lwebpdecoder -lwebp $(LIBSHARPYUV) -lsbml-static -lgeotiff -lproj -lsqlite3 -lbz2 -lcrypt32 -lwldap32 -lsecur32 all: clean winlibs winlibs: cp postgis_config.win postgis_config.h mkdir -p ../inst cp -r "$(R_TOOLS_SOFT)/share/gdal" ../inst/ cp -r "$(R_TOOLS_SOFT)/share/proj" ../inst/ $(SHLIB): $(STATLIB) $(STATLIB): $(OBJECTS_LIBLWGEOM) $(OBJECTS_LIBLWGEOM): winlibs CXX_STD = CXX clean: rm -f $(SHLIB) $(OBJECTS) .PHONY: all winlibs clean lwgeom/src/postgis_config.win0000644000176200001440000001041513773172540016101 0ustar liggesusers/* postgis_config.h. Generated from postgis_config.h.in by configure. */ #ifndef POSTGIS_CONFIG_H #define POSTGIS_CONFIG_H 1 #include "postgis_svn_revision.h" /* Manually manipulate the POSTGIS_DEBUG_LEVEL, it is not affected by the configure process */ #define POSTGIS_DEBUG_LEVEL 0 /* Define to 1 to enable memory checks in pointarray management. */ #define PARANOIA_LEVEL 0 /* Define to 1 if translation of program messages to the user's native language is requested. */ #define ENABLE_NLS 1 /* Define for some functions we are interested in */ #define HAVE_FSEEKO 1 /* Define if the GNU gettext() function is already present or preinstalled. */ #define HAVE_GETTEXT 1 /* Define if the build is big endian */ /* #undef WORDS_BIGENDIAN */ /* Define if you have the iconv() function and it works. */ #define HAVE_ICONV 1 /* Define to 1 if you have the `iconvctl' function. */ /* #undef HAVE_ICONVCTL */ /* ieeefp.h header */ #define HAVE_IEEEFP_H 0 /* Define to 1 if you have the `geos_c' library (-lgeos_c). */ #define HAVE_LIBGEOS_C 1 /* Define to 1 if you have the `libiconvctl' function. */ /* #undef HAVE_LIBICONVCTL */ /* Define to 1 if libprotobuf-c is present */ /* #undef HAVE_LIBPROTOBUF */ /* Define to 1 if protobuf_c_version() is present */ /* #undef HAVE_PROTOBUF_C_VERSION */ /* Numeric version number for libprotobuf */ /* #undef LIBPROTOBUF_VERSION */ /* Define to 1 if libprotobuf-c is >= version 1.1 */ /* #undef HAVE_GEOBUF */ /* Define to 1 if libjson is present */ #define HAVE_LIBJSON 1 /* Define to 1 if you have the `pq' library (-lpq). */ #define HAVE_LIBPQ 1 /* Define to 1 if you have the `proj' library (-lproj). */ #define HAVE_LIBPROJ 1 /* Define to 1 if you have the `xml2' library (-lxml2). */ #define HAVE_LIBXML2 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_PARSER_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_TREE_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATHINTERNALS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBXML_XPATH_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if wagyu is being built */ #define HAVE_WAGYU 1 /* Define to 1 if sfcgal is being built */ /* #undef HAVE_SFCGAL */ /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Location of PostgreSQL locale directory */ #define PGSQL_LOCALEDIR "/usr/share/locale" /* PostGIS build date */ #define POSTGIS_BUILD_DATE "2020-01-28 17:06:21" /* SFCGAL library version at buil time */ /* #undef POSTGIS_SFCGAL_VERSION */ /* GDAL library version */ /* #undef POSTGIS_GDAL_VERSION */ /* GEOS library version */ #define POSTGIS_GEOS_VERSION 38 /* PostGIS libxml2 version */ #define POSTGIS_LIBXML2_VERSION "2.9.4" /* PostGIS library version */ #define POSTGIS_LIB_VERSION "3.0.0" /* PostGIS major version */ #define POSTGIS_MAJOR_VERSION "3" /* PostGIS minor version */ #define POSTGIS_MINOR_VERSION "0" /* PostGIS micro version */ #define POSTGIS_MICRO_VERSION "0" /* PostgreSQL server version */ #define POSTGIS_PGSQL_VERSION 100 /* PROJ library version */ #define POSTGIS_PROJ_VERSION 63 /* PostGIS Raster build date */ /* #undef POSTGIS_RASTER_BUILD_DATE */ /* PostGIS Raster library version */ /* #undef POSTGIS_RASTER_LIB_VERSION */ /* PostGIS Raster major version */ /* #undef POSTGIS_RASTER_MAJOR_VERSION */ /* PostGIS Raster micro version */ /* #undef POSTGIS_RASTER_MICRO_VERSION */ /* PostGIS Raster minor version */ /* #undef POSTGIS_RASTER_MINOR_VERSION */ /* PostGIS Raster scripts version */ /* #undef POSTGIS_RASTER_SCRIPTS_VERSION */ /* PostGIS Raster version */ /* #undef POSTGIS_RASTER_VERSION */ /* Define to 1 if a warning is outputted every time a double is truncated */ /* #undef POSTGIS_RASTER_WARN_ON_TRUNCATION */ /* PostGIS scripts version */ #define POSTGIS_SCRIPTS_VERSION "3.0.0" /* PostGIS version */ #define POSTGIS_VERSION "3.0 USE_GEOS=1 USE_PROJ=1 USE_STATS=1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ #define YYTEXT_POINTER 1 #endif /* POSTGIS_CONFIG_H */ lwgeom/src/Makevars.in0000644000176200001440000000412414426521131014434 0ustar liggesusersPKG_CPPFLAGS=@PKG_CPPFLAGS@ PKG_LIBS=@PKG_LIBS@ CXX_STD = CXX OBJECTS_LIBLWGEOM= \ liblwgeom/varint.o \ liblwgeom/lwout_twkb.o \ liblwgeom/lwpsurface.o \ liblwgeom/lwtriangle.o \ liblwgeom/lwmpoly.o \ liblwgeom/lookup3.o \ liblwgeom/lwin_wkt.o \ liblwgeom/gserialized1.o \ liblwgeom/lwgeom.o \ liblwgeom/gserialized2.o \ liblwgeom/lwstroke.o \ liblwgeom/lwtin.o \ liblwgeom/lwin_twkb.o \ liblwgeom/lwgeom_geos_cluster.o \ liblwgeom/lwmsurface.o \ liblwgeom/lwgeom_wrapx.o \ liblwgeom/lwiterator.o \ liblwgeom/lwgeom_geos_node.o \ liblwgeom/lwout_geojson.o \ liblwgeom/lwgeom_debug.o \ liblwgeom/lwgeom_median.o \ liblwgeom/lwmval.o \ liblwgeom/lwkmeans.o \ liblwgeom/lwgeom_geos.o \ liblwgeom/lwout_kml.o \ liblwgeom/lwutil.o \ liblwgeom/lwprint.o \ liblwgeom/lwrandom.o \ liblwgeom/lwmline.o \ liblwgeom/lwgeodetic_tree.o \ liblwgeom/lwline.o \ liblwgeom/bytebuffer.o \ liblwgeom/lwgeodetic.o \ liblwgeom/measures.o \ liblwgeom/lwgeom_api.o \ liblwgeom/lwmcurve.o \ liblwgeom/lwcollection.o \ liblwgeom/gbox.o \ liblwgeom/lwspheroid.o \ liblwgeom/lwout_svg.o \ liblwgeom/lwin_encoded_polyline.o \ liblwgeom/lwout_encoded_polyline.o \ liblwgeom/lwgeom_geos_split.o \ liblwgeom/effectivearea.o \ liblwgeom/lwboundingcircle.o \ liblwgeom/lwcurvepoly.o \ liblwgeom/lwlinearreferencing.o \ liblwgeom/lwunionfind.o \ liblwgeom/lwchaikins.o \ liblwgeom/lwalgorithm.o \ liblwgeom/lwhomogenize.o \ liblwgeom/lwgeom_geos_clean.o \ liblwgeom/measures3d.o \ liblwgeom/lwout_x3d.o \ liblwgeom/lwgeom_transform.o \ liblwgeom/lwin_wkt_lex.o \ liblwgeom/lwmpoint.o \ liblwgeom/stringbuffer.o \ liblwgeom/lwcompound.o \ liblwgeom/gserialized.o \ liblwgeom/lwout_wkt.o \ liblwgeom/lwin_wkb.o \ liblwgeom/ptarray.o \ liblwgeom/lwout_wkb.o \ liblwgeom/lwpoly.o \ liblwgeom/lwpoint.o \ liblwgeom/lwout_gml.o \ liblwgeom/lwgeom_topo.o \ liblwgeom/lwcircstring.o \ liblwgeom/lwin_wkt_parse.o OBJECTS_RCPP= \ geodetic.o \ lwgeom.o \ RcppExports.o \ proj.o \ geos.o \ io.o \ sub.o OBJECTS=@OBJECTS@ lwgeom/src/RcppExports.cpp0000644000176200001440000004030314406665625015346 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include using namespace Rcpp; #ifdef RCPP_USE_GLOBAL_ROSTREAM Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif // CPL_geodetic_area Rcpp::NumericVector CPL_geodetic_area(Rcpp::List sfc, double semi_major, double inv_flattening); RcppExport SEXP _lwgeom_CPL_geodetic_area(SEXP sfcSEXP, SEXP semi_majorSEXP, SEXP inv_flatteningSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< double >::type semi_major(semi_majorSEXP); Rcpp::traits::input_parameter< double >::type inv_flattening(inv_flatteningSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_area(sfc, semi_major, inv_flattening)); return rcpp_result_gen; END_RCPP } // CPL_geodetic_length Rcpp::NumericVector CPL_geodetic_length(Rcpp::List sfc, double semi_major, double inv_flattening); RcppExport SEXP _lwgeom_CPL_geodetic_length(SEXP sfcSEXP, SEXP semi_majorSEXP, SEXP inv_flatteningSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< double >::type semi_major(semi_majorSEXP); Rcpp::traits::input_parameter< double >::type inv_flattening(inv_flatteningSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_length(sfc, semi_major, inv_flattening)); return rcpp_result_gen; END_RCPP } // CPL_geodetic_azimuth Rcpp::NumericVector CPL_geodetic_azimuth(Rcpp::List sfc, double semi_major, double inv_flattening); RcppExport SEXP _lwgeom_CPL_geodetic_azimuth(SEXP sfcSEXP, SEXP semi_majorSEXP, SEXP inv_flatteningSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< double >::type semi_major(semi_majorSEXP); Rcpp::traits::input_parameter< double >::type inv_flattening(inv_flatteningSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_azimuth(sfc, semi_major, inv_flattening)); return rcpp_result_gen; END_RCPP } // CPL_geodetic_segmentize Rcpp::List CPL_geodetic_segmentize(Rcpp::List sfc, double max_seg_length); RcppExport SEXP _lwgeom_CPL_geodetic_segmentize(SEXP sfcSEXP, SEXP max_seg_lengthSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< double >::type max_seg_length(max_seg_lengthSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_segmentize(sfc, max_seg_length)); return rcpp_result_gen; END_RCPP } // CPL_geodetic_covers Rcpp::List CPL_geodetic_covers(Rcpp::List sfc1, Rcpp::List sfc2); RcppExport SEXP _lwgeom_CPL_geodetic_covers(SEXP sfc1SEXP, SEXP sfc2SEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc1(sfc1SEXP); Rcpp::traits::input_parameter< Rcpp::List >::type sfc2(sfc2SEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_covers(sfc1, sfc2)); return rcpp_result_gen; END_RCPP } // CPL_geodetic_distance Rcpp::List CPL_geodetic_distance(Rcpp::List sfc1, Rcpp::List sfc2, double semi_major, double inv_flattening, double tolerance, bool sparse, double semi_minor); RcppExport SEXP _lwgeom_CPL_geodetic_distance(SEXP sfc1SEXP, SEXP sfc2SEXP, SEXP semi_majorSEXP, SEXP inv_flatteningSEXP, SEXP toleranceSEXP, SEXP sparseSEXP, SEXP semi_minorSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc1(sfc1SEXP); Rcpp::traits::input_parameter< Rcpp::List >::type sfc2(sfc2SEXP); Rcpp::traits::input_parameter< double >::type semi_major(semi_majorSEXP); Rcpp::traits::input_parameter< double >::type inv_flattening(inv_flatteningSEXP); Rcpp::traits::input_parameter< double >::type tolerance(toleranceSEXP); Rcpp::traits::input_parameter< bool >::type sparse(sparseSEXP); Rcpp::traits::input_parameter< double >::type semi_minor(semi_minorSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geodetic_distance(sfc1, sfc2, semi_major, inv_flattening, tolerance, sparse, semi_minor)); return rcpp_result_gen; END_RCPP } // CPL_geos_version std::string CPL_geos_version(bool b); RcppExport SEXP _lwgeom_CPL_geos_version(SEXP bSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< bool >::type b(bSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geos_version(b)); return rcpp_result_gen; END_RCPP } // CPL_init_lwgeom Rcpp::List CPL_init_lwgeom(Rcpp::List l); RcppExport SEXP _lwgeom_CPL_init_lwgeom(SEXP lSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type l(lSEXP); rcpp_result_gen = Rcpp::wrap(CPL_init_lwgeom(l)); return rcpp_result_gen; END_RCPP } // CPL_lwgeom_version Rcpp::CharacterVector CPL_lwgeom_version(bool b); RcppExport SEXP _lwgeom_CPL_lwgeom_version(SEXP bSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< bool >::type b(bSEXP); rcpp_result_gen = Rcpp::wrap(CPL_lwgeom_version(b)); return rcpp_result_gen; END_RCPP } // CPL_sfc_from_twkb Rcpp::List CPL_sfc_from_twkb(Rcpp::List twkb); RcppExport SEXP _lwgeom_CPL_sfc_from_twkb(SEXP twkbSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type twkb(twkbSEXP); rcpp_result_gen = Rcpp::wrap(CPL_sfc_from_twkb(twkb)); return rcpp_result_gen; END_RCPP } // CPL_make_valid Rcpp::List CPL_make_valid(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_make_valid(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_make_valid(sfc)); return rcpp_result_gen; END_RCPP } // CPL_split Rcpp::List CPL_split(Rcpp::List sfc, Rcpp::List blade); RcppExport SEXP _lwgeom_CPL_split(SEXP sfcSEXP, SEXP bladeSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< Rcpp::List >::type blade(bladeSEXP); rcpp_result_gen = Rcpp::wrap(CPL_split(sfc, blade)); return rcpp_result_gen; END_RCPP } // CPL_wrap_x Rcpp::List CPL_wrap_x(Rcpp::List sfc, Rcpp::NumericVector wrap, Rcpp::NumericVector move); RcppExport SEXP _lwgeom_CPL_wrap_x(SEXP sfcSEXP, SEXP wrapSEXP, SEXP moveSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector >::type wrap(wrapSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector >::type move(moveSEXP); rcpp_result_gen = Rcpp::wrap(CPL_wrap_x(sfc, wrap, move)); return rcpp_result_gen; END_RCPP } // CPL_geohash Rcpp::CharacterVector CPL_geohash(Rcpp::List sfc, int prec); RcppExport SEXP _lwgeom_CPL_geohash(SEXP sfcSEXP, SEXP precSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< int >::type prec(precSEXP); rcpp_result_gen = Rcpp::wrap(CPL_geohash(sfc, prec)); return rcpp_result_gen; END_RCPP } // CPL_lwgeom_transform Rcpp::List CPL_lwgeom_transform(Rcpp::List sfc, Rcpp::CharacterVector p4s); RcppExport SEXP _lwgeom_CPL_lwgeom_transform(SEXP sfcSEXP, SEXP p4sSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< Rcpp::CharacterVector >::type p4s(p4sSEXP); rcpp_result_gen = Rcpp::wrap(CPL_lwgeom_transform(sfc, p4s)); return rcpp_result_gen; END_RCPP } // CPL_minimum_bounding_circle Rcpp::List CPL_minimum_bounding_circle(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_minimum_bounding_circle(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_minimum_bounding_circle(sfc)); return rcpp_result_gen; END_RCPP } // CPL_subdivide Rcpp::List CPL_subdivide(Rcpp::List sfc, int max_vertices); RcppExport SEXP _lwgeom_CPL_subdivide(SEXP sfcSEXP, SEXP max_verticesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< int >::type max_vertices(max_verticesSEXP); rcpp_result_gen = Rcpp::wrap(CPL_subdivide(sfc, max_vertices)); return rcpp_result_gen; END_RCPP } // CPL_snap_to_grid Rcpp::List CPL_snap_to_grid(Rcpp::List sfc, Rcpp::NumericVector origin, Rcpp::NumericVector size); RcppExport SEXP _lwgeom_CPL_snap_to_grid(SEXP sfcSEXP, SEXP originSEXP, SEXP sizeSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector >::type origin(originSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector >::type size(sizeSEXP); rcpp_result_gen = Rcpp::wrap(CPL_snap_to_grid(sfc, origin, size)); return rcpp_result_gen; END_RCPP } // CPL_perimeter Rcpp::NumericVector CPL_perimeter(Rcpp::List sfc, bool do2d); RcppExport SEXP _lwgeom_CPL_perimeter(SEXP sfcSEXP, SEXP do2dSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< bool >::type do2d(do2dSEXP); rcpp_result_gen = Rcpp::wrap(CPL_perimeter(sfc, do2d)); return rcpp_result_gen; END_RCPP } // CPL_is_polygon_cw Rcpp::LogicalVector CPL_is_polygon_cw(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_is_polygon_cw(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_is_polygon_cw(sfc)); return rcpp_result_gen; END_RCPP } // CPL_force_polygon_cw Rcpp::List CPL_force_polygon_cw(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_force_polygon_cw(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_force_polygon_cw(sfc)); return rcpp_result_gen; END_RCPP } // CPL_startpoint Rcpp::NumericMatrix CPL_startpoint(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_startpoint(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_startpoint(sfc)); return rcpp_result_gen; END_RCPP } // CPL_endpoint Rcpp::NumericMatrix CPL_endpoint(Rcpp::List sfc); RcppExport SEXP _lwgeom_CPL_endpoint(SEXP sfcSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); rcpp_result_gen = Rcpp::wrap(CPL_endpoint(sfc)); return rcpp_result_gen; END_RCPP } // CPL_sfc_to_wkt Rcpp::CharacterVector CPL_sfc_to_wkt(Rcpp::List sfc, Rcpp::IntegerVector precision); RcppExport SEXP _lwgeom_CPL_sfc_to_wkt(SEXP sfcSEXP, SEXP precisionSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type precision(precisionSEXP); rcpp_result_gen = Rcpp::wrap(CPL_sfc_to_wkt(sfc, precision)); return rcpp_result_gen; END_RCPP } // CPL_proj_version std::string CPL_proj_version(bool b); RcppExport SEXP _lwgeom_CPL_proj_version(SEXP bSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< bool >::type b(bSEXP); rcpp_result_gen = Rcpp::wrap(CPL_proj_version(b)); return rcpp_result_gen; END_RCPP } // CPL_use_proj4_init_rules Rcpp::LogicalVector CPL_use_proj4_init_rules(Rcpp::IntegerVector v); RcppExport SEXP _lwgeom_CPL_use_proj4_init_rules(SEXP vSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type v(vSEXP); rcpp_result_gen = Rcpp::wrap(CPL_use_proj4_init_rules(v)); return rcpp_result_gen; END_RCPP } // CPL_set_data_dir Rcpp::LogicalVector CPL_set_data_dir(std::string data_dir); RcppExport SEXP _lwgeom_CPL_set_data_dir(SEXP data_dirSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::string >::type data_dir(data_dirSEXP); rcpp_result_gen = Rcpp::wrap(CPL_set_data_dir(data_dir)); return rcpp_result_gen; END_RCPP } // CPL_linesubstring Rcpp::List CPL_linesubstring(Rcpp::List sfc, double from, double to, double tolerance); RcppExport SEXP _lwgeom_CPL_linesubstring(SEXP sfcSEXP, SEXP fromSEXP, SEXP toSEXP, SEXP toleranceSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::List >::type sfc(sfcSEXP); Rcpp::traits::input_parameter< double >::type from(fromSEXP); Rcpp::traits::input_parameter< double >::type to(toSEXP); Rcpp::traits::input_parameter< double >::type tolerance(toleranceSEXP); rcpp_result_gen = Rcpp::wrap(CPL_linesubstring(sfc, from, to, tolerance)); return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { {"_lwgeom_CPL_geodetic_area", (DL_FUNC) &_lwgeom_CPL_geodetic_area, 3}, {"_lwgeom_CPL_geodetic_length", (DL_FUNC) &_lwgeom_CPL_geodetic_length, 3}, {"_lwgeom_CPL_geodetic_azimuth", (DL_FUNC) &_lwgeom_CPL_geodetic_azimuth, 3}, {"_lwgeom_CPL_geodetic_segmentize", (DL_FUNC) &_lwgeom_CPL_geodetic_segmentize, 2}, {"_lwgeom_CPL_geodetic_covers", (DL_FUNC) &_lwgeom_CPL_geodetic_covers, 2}, {"_lwgeom_CPL_geodetic_distance", (DL_FUNC) &_lwgeom_CPL_geodetic_distance, 7}, {"_lwgeom_CPL_geos_version", (DL_FUNC) &_lwgeom_CPL_geos_version, 1}, {"_lwgeom_CPL_init_lwgeom", (DL_FUNC) &_lwgeom_CPL_init_lwgeom, 1}, {"_lwgeom_CPL_lwgeom_version", (DL_FUNC) &_lwgeom_CPL_lwgeom_version, 1}, {"_lwgeom_CPL_sfc_from_twkb", (DL_FUNC) &_lwgeom_CPL_sfc_from_twkb, 1}, {"_lwgeom_CPL_make_valid", (DL_FUNC) &_lwgeom_CPL_make_valid, 1}, {"_lwgeom_CPL_split", (DL_FUNC) &_lwgeom_CPL_split, 2}, {"_lwgeom_CPL_wrap_x", (DL_FUNC) &_lwgeom_CPL_wrap_x, 3}, {"_lwgeom_CPL_geohash", (DL_FUNC) &_lwgeom_CPL_geohash, 2}, {"_lwgeom_CPL_lwgeom_transform", (DL_FUNC) &_lwgeom_CPL_lwgeom_transform, 2}, {"_lwgeom_CPL_minimum_bounding_circle", (DL_FUNC) &_lwgeom_CPL_minimum_bounding_circle, 1}, {"_lwgeom_CPL_subdivide", (DL_FUNC) &_lwgeom_CPL_subdivide, 2}, {"_lwgeom_CPL_snap_to_grid", (DL_FUNC) &_lwgeom_CPL_snap_to_grid, 3}, {"_lwgeom_CPL_perimeter", (DL_FUNC) &_lwgeom_CPL_perimeter, 2}, {"_lwgeom_CPL_is_polygon_cw", (DL_FUNC) &_lwgeom_CPL_is_polygon_cw, 1}, {"_lwgeom_CPL_force_polygon_cw", (DL_FUNC) &_lwgeom_CPL_force_polygon_cw, 1}, {"_lwgeom_CPL_startpoint", (DL_FUNC) &_lwgeom_CPL_startpoint, 1}, {"_lwgeom_CPL_endpoint", (DL_FUNC) &_lwgeom_CPL_endpoint, 1}, {"_lwgeom_CPL_sfc_to_wkt", (DL_FUNC) &_lwgeom_CPL_sfc_to_wkt, 2}, {"_lwgeom_CPL_proj_version", (DL_FUNC) &_lwgeom_CPL_proj_version, 1}, {"_lwgeom_CPL_use_proj4_init_rules", (DL_FUNC) &_lwgeom_CPL_use_proj4_init_rules, 1}, {"_lwgeom_CPL_set_data_dir", (DL_FUNC) &_lwgeom_CPL_set_data_dir, 1}, {"_lwgeom_CPL_linesubstring", (DL_FUNC) &_lwgeom_CPL_linesubstring, 4}, {NULL, NULL, 0} }; RcppExport void R_init_lwgeom(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } lwgeom/R/0000755000176200001440000000000014406665625011763 5ustar liggesuserslwgeom/R/snap_to_grid.R0000644000176200001440000000260213773172540014551 0ustar liggesusers#' Snap geometries to a grid #' #' @name st_snap_to_grid #' @param x object with geometries to be snapped #' @param size numeric or (length) units object; grid cell size in x-, y- (and possibly z- and m-) directions #' @param origin numeric; origin of the grid #' @return object of the same class as \code{x} #' @examples #' # obtain data #' library(sf) #' x = st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE)[1, ] %>% #' st_geometry %>% #' st_transform(3395) #' #' # snap to a grid of 5000 m #' err = try(y <- st_snap_to_grid(x, 5000)) #' #' # plot data for visual comparison #' if (!inherits(err, "try-error")) { #' opar = par(mfrow = c(1, 2)) #' plot(x, main = "orginal data") #' plot(y, main = "snapped to 5000 m") #' par(opar) #' } #' @export st_snap_to_grid = function(x, size, origin) UseMethod("st_snap_to_grid") #' @export st_snap_to_grid.sfg = function(x, size, origin = st_point(rep(0.0,4))) { st_snap_to_grid(st_geometry(x), size, origin)[[1]] } #' @export st_snap_to_grid.sfc = function(x, size, origin = st_point(rep(0.0,4))) { size = rep(as.numeric(size), length.out = 4) stopifnot(!isTRUE(st_is_longlat(x))) # FIXME units(size) = as_units("m") st_sfc(CPL_snap_to_grid(x, origin, size), crs = st_crs(x)) } #' @export st_snap_to_grid.sf = function(x, size, origin = st_point(rep(0.0,4))) { st_set_geometry(x, st_snap_to_grid(st_geometry(x), size, origin)) } lwgeom/R/subdivide.R0000644000176200001440000000150613773172540014061 0ustar liggesusers#' Return a collection of geometries resulting by subdividing a geometry #' #' @name st_subdivide #' @param x object with geometries to be subdivided #' @param max_vertices integer; maximum size of the subgeometries (at least 8) #' @return object of the same class as \code{x} #' @examples #' library(sf) #' demo(nc, ask = FALSE, echo = FALSE) #' x = st_subdivide(nc, 10) #' plot(x[1]) #' @export st_subdivide = function(x, max_vertices) UseMethod("st_subdivide") #' @export st_subdivide.sfg = function(x, max_vertices = 256) { st_subdivide(st_geometry(x), max_vertices)[[1]] } #' @export st_subdivide.sfc = function(x, max_vertices = 256) { st_sfc(CPL_subdivide(x, max_vertices), crs = st_crs(x)) } #' @export st_subdivide.sf = function(x, max_vertices = 256) { st_set_geometry(x, st_subdivide(st_geometry(x), max_vertices)) } lwgeom/R/wkt.R0000644000176200001440000000256514406665546012725 0ustar liggesusers#' Return Well-known Text representation of simple feature geometry #' #' Return Well-known Text representation of simple feature geometry or coordinate reference system #' @param x object of class `sfg`, `sfc`, or `sf` #' @param digits integer; number of decimal digits to print #' @param ... ignored #' @param EWKT logical; use PostGIS Enhanced WKT (includes srid) #' @name st_astext #' @details The returned WKT representation of simple feature geometry conforms to the #' [simple features access](https://www.ogc.org/standard/sfa/) specification and extensions (if `EWKT = TRUE`), #' [known as EWKT](http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT), supported by #' PostGIS and other simple features implementations for addition of SRID to a WKT string. #' @md #' @examples #' library(sf) #' pt <- st_sfc(st_point(c(1.0002,2.3030303)), crs = 4326) #' st_astext(pt, 3) #' st_asewkt(pt, 3) #' @export st_astext <- function(x, digits = options("digits"), ..., EWKT = FALSE) { if (! EWKT && !inherits(x, "sfg")) st_crs(x) <- NA_crs_ CPL_sfc_to_wkt(st_geometry(x), as.integer(digits)) } #' @name st_astext #' @inheritParams st_astext #' @details `st_asewkt()` returns the Well-Known Text (WKT) representation of #' the geometry with SRID meta data. #' @md #' @export st_asewkt <- function(x, digits = options("digits")) { st_astext(x, digits = digits, EWKT = TRUE) } lwgeom/R/split.R0000755000176200001440000000521013773172540013235 0ustar liggesusers#' Return a collection of geometries resulting by splitting a geometry #' #' @name st_split #' @param x object with geometries to be splitted #' @param y object split with (blade); if \code{y} contains more than one feature geometry, the geometries are \link[sf:geos_combine]{st_combine} 'd #' @return object of the same class as \code{x} #' @examples #' library(sf) #' l = st_as_sfc('MULTILINESTRING((10 10, 190 190), (15 15, 30 30, 100 90))') #' pt = st_sfc(st_point(c(30,30))) #' st_split(l, pt) #' @export st_split = function(x, y) UseMethod("st_split") #' @export st_split.sfg = function(x, y) { st_split(st_geometry(x), st_geometry(y))[[1]] } #' @export st_split.sfc = function(x, y) { if (length(y) > 1) y = sf::st_combine(y) if (inherits(x, "sfc_POLYGON") || inherits(x, "sfc_MULTIPOLYGON")) stopifnot(inherits(y, "sfc_LINESTRING") || inherits(y, "sfc_MULTILINESTRING")) else stopifnot(inherits(x, "sfc_LINESTRING") || inherits(x, "sfc_MULTILINESTRING")) st_sfc(CPL_split(x, st_geometry(y)), crs = st_crs(x)) } #' @export st_split.sf = function(x, y) { st_set_geometry(x, st_split(st_geometry(x), y)) } #' get substring from linestring #' @export #' @param x object of class \code{sfc}, \code{sf} or \code{sfg} #' @param from relative distance from origin (in [0,1]) #' @param to relative distance from origin (in [0,1]) #' @param ... ignored #' @param tolerance tolerance parameter, when to snap to line node node #' @return object of class \code{sfc} #' @examples #' library(sf) #' lines = st_sfc(st_linestring(rbind(c(0,0), c(1,2), c(2,0))), crs = 4326) #' spl = st_linesubstring(lines, 0.2, 0.8) # should warn #' plot(st_geometry(lines), col = 'red', lwd = 3) #' plot(spl, col = 'black', lwd = 3, add = TRUE) #' st_linesubstring(lines, 0.49999, 0.8) # three points #' st_linesubstring(lines, 0.49999, 0.8, 0.001) # two points: snap start to second node st_linesubstring = function(x, from, to, tolerance, ...) UseMethod("st_linesubstring") #' @export st_linesubstring.sfc = function(x, from, to, tolerance = 0.0, ...) { if (isTRUE(st_is_longlat(x))) warning("st_linesubstring does not follow a geodesic; you may want to use st_geod_segmentize first") st_sfc(CPL_linesubstring(x, from, to, tolerance), crs = st_crs(x)) } #' @export st_linesubstring.sf = function(x, from, to, tolerance = 0.0, ...) { if (isTRUE(st_is_longlat(x))) warning("st_linesubstring does not follow a geodesic; you may want to use st_geod_segmentize first") st_set_geometry(x, st_linesubstring(st_geometry(x), from, to, tolerance)) } #' @export st_linesubstring.sfg = function(x, from, to, tolerance = 0.0, ...) { CPL_linesubstring(st_geometry(x), from, to, tolerance)[[1]] } lwgeom/R/wrap_x.R0000644000176200001440000000161114076540103013367 0ustar liggesusers#' Splits input geometries by a vertical line and moves components falling on #' one side of that line by a fixed amount #' #' @name st_wrap_x #' @param x object with geometries to be split #' @param wrap x value of split line #' @param move amount by which geometries falling to the left of the line #' should be translated to the right #' @return object of the same class as \code{x} #' @examples #' library(sf) #' demo(nc, ask = FALSE, echo = FALSE) #' x = st_wrap_x(nc, -78, 10) #' plot(x[1]) #' @export st_wrap_x = function(x, wrap, move) UseMethod("st_wrap_x") #' @export st_wrap_x.sfg = function(x, wrap, move) { st_wrap_x(st_geometry(x), wrap, move)[[1]] } #' @export st_wrap_x.sfc = function(x, wrap, move) { st_sfc(CPL_wrap_x(x, wrap, move), crs = st_crs(x)) } #' @export st_wrap_x.sf = function(x, wrap, move) { st_set_geometry(x, st_wrap_x(st_geometry(x), wrap, move)) } lwgeom/R/clockwise.R0000644000176200001440000000407113773172540014066 0ustar liggesusers#' Force a POLYGON or MULTIPOLYGON to be clockwise #' #' Check if a POLYGON or MULTIPOLYGON is clockwise, and if not make it so. #' According to the 'Right-hand-rule', outer rings should be clockwise, and #' inner holes should be counter-clockwise #' #' #' @name st_force_polygon_cw #' @param x object with polygon geometries #' @return object of the same class as \code{x} #' @export #' #' @examples #' library(sf) #' polys <- st_sf(cw = c(FALSE, TRUE), #' st_as_sfc(c('POLYGON ((0 0, 1 0, 1 1, 0 0))', #' 'POLYGON ((1 1, 2 2, 2 1, 1 1))'))) #' #' st_force_polygon_cw(polys) #' st_force_polygon_cw(st_geometry(polys)) #' st_force_polygon_cw(st_geometry(polys)[[1]]) st_force_polygon_cw = function(x) UseMethod("st_force_polygon_cw") #' @export st_force_polygon_cw.sfg = function(x) { st_force_polygon_cw(st_geometry(x))[[1]] } #' @export st_force_polygon_cw.sfc = function(x) { st_sfc(CPL_force_polygon_cw(x), crs = st_crs(x)) } #' @export st_force_polygon_cw.sf = function(x) { st_set_geometry(x, st_force_polygon_cw(st_geometry(x))) } #' Check if a POLYGON or MULTIPOLYGON is clockwise #' #' Check if a POLYGON or MULTIPOLYGON is clockwise. According to the #' 'Right-hand-rule', outer rings should be clockwise, and inner holes #' should be counter-clockwise #' #' @name st_is_polygon_cw #' @param x object with polygon geometries #' @return logical with length the same number of features in `x` #' @export #' #' @examples #' library(sf) #' polys <- st_sf(cw = c(FALSE, TRUE), #' st_as_sfc(c('POLYGON ((0 0, 1 0, 1 1, 0 0))', #' 'POLYGON ((1 1, 2 2, 2 1, 1 1))'))) #' #' st_is_polygon_cw(polys) #' st_is_polygon_cw(st_geometry(polys)) #' st_is_polygon_cw(st_geometry(polys)[[1]]) st_is_polygon_cw = function(x) UseMethod("st_is_polygon_cw") #' @export st_is_polygon_cw.sfg = function(x) { st_is_polygon_cw(st_geometry(x))[[1]] } #' @export st_is_polygon_cw.sfc = function(x) { CPL_is_polygon_cw(x) } #' @export st_is_polygon_cw.sf = function(x) { st_is_polygon_cw(st_geometry(x)) } lwgeom/R/geod.R0000644000176200001440000001062413773172540013022 0ustar liggesusers#' liblwgeom geodetic functions #' #' liblwgeom geodetic functions for length, area, segmentizing, covers #' @name geod #' @param x object of class \code{sf}, \code{sfc} or \code{sfg} #' @export #' @details \code{st_area} will give an error message when the area spans the equator and \code{lwgeom} is linked to a proj.4 version older than 4.9.0 (see \link{lwgeom_extSoftVersion}) #' @examples #' library(sf) #' nc = st_read(system.file("gpkg/nc.gpkg", package="sf")) #' st_geod_area(nc[1:3,]) #' # st_area(nc[1:3,]) st_geod_area = function(x) { stopifnot(st_is_longlat(x)) p = st_crs(st_geometry(x), parameters = TRUE) ret = CPL_geodetic_area(st_geometry(x), p$SemiMajor, p$InvFlattening) units(ret) = units(p$SemiMajor^2) ret } #' @name geod #' @export #' @examples #' l = st_sfc(st_linestring(rbind(c(7,52), c(8,53))), crs = 4326) #' st_geod_length(l) st_geod_length = function(x) { stopifnot(st_is_longlat(x)) p = st_crs(st_geometry(x), parameters = TRUE) ret = CPL_geodetic_length(st_geometry(x), p$SemiMajor, p$InvFlattening) units(ret) = units(p$SemiMajor) ret } #' @name geod #' @param max_seg_length segment length in degree, radians, or as a length unit (e.g., m) #' @export #' @examples #' library(units) #' pol = st_polygon(list(rbind(c(0,0), c(0,60), c(60,60), c(0,0)))) #' x = st_sfc(pol, crs = 4326) #' seg = st_geod_segmentize(x[1], set_units(10, km)) #' plot(seg, graticule = TRUE, axes = TRUE) #' @details #' longitude coordinates returned are rescaled to [-180,180) st_geod_segmentize = function(x, max_seg_length) { stopifnot(st_is_longlat(x)) p = st_crs(st_geometry(x), parameters = TRUE) if (inherits(max_seg_length, "units")) { tr = try(units(max_seg_length) <- as_units("rad"), silent = TRUE) if (inherits(tr, "try-error")) { units(max_seg_length) = units(p$SemiMajor) # -> m max_seg_length = max_seg_length / p$SemiMajor # m -> rad } } else stop("st_geod_segmentize needs a max_seg_length with units rad, degree, or a length unit") ret = st_sfc(CPL_geodetic_segmentize(st_geometry(x), max_seg_length)) st_set_crs((ret + c(180,90)) %% 360 - c(180, 90), st_crs(x)) # rescale lon to [-180,180) } #' @name geod #' @param y object of class \code{sf}, \code{sfc} or \code{sfg} #' @param sparse logical; if \code{TRUE}, return a sparse matrix (object of class \code{sgbp}), otherwise, return a dense logical matrix. #' @export #' @examples #' pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) #' pt = st_point(c(0,90)) #' x = st_sfc(pole, pt, crs = 4326) #' st_geod_covers(x[c(1,1,1)], x[c(2,2,2,2)]) st_geod_covers = function(x, y, sparse = TRUE) { stopifnot(st_is_longlat(x)) stopifnot(st_is_longlat(y)) if (!all(st_dimension(x) == 2)) stop("argument x must contain only polygons") if (!all(st_dimension(y) == 0)) stop("argument y must contain only points") if (is.null(id <- row.names(x))) id = as.character(seq_along(st_geometry(x))) ret = structure(CPL_geodetic_covers(st_geometry(x), st_geometry(y)), predicate = "covers", region.id = id, ncol = length(st_geometry(y)), class = "sgbp") if (sparse) ret else as.matrix(ret) } #' @name geod #' @export st_geod_covered_by = function(x, y, sparse = TRUE) { ret = structure(t(st_geod_covers(y, x)), predicate = "covered_by") if (sparse) ret else as.matrix(ret) } #' @name geod #' @export #' @param tolerance double or length \code{units} value: if positive, the first distance less than \code{tolerance} is returned, rather than the true distance #' @note this function should is used by \link[sf:geos_measures]{st_distance}, do not use it directly #' @examples #' pole = st_polygon(list(rbind(c(0,80), c(120,80), c(240,80), c(0,80)))) #' pt = st_point(c(30,70)) #' x = st_sfc(pole, pt, crs = 4326) #' st_geod_distance(x, x) st_geod_distance = function(x, y, tolerance = 0.0, sparse = FALSE) { stopifnot(st_is_longlat(x)) stopifnot(st_crs(x) == st_crs(y)) p = st_crs(st_geometry(x), parameters = TRUE) SemiMinor = if (is.null(p$SemiMinor)) -1.0 else p$SemiMinor units(tolerance) = as_units("m") ret = CPL_geodetic_distance(st_geometry(x), st_geometry(y), p$SemiMajor, p$InvFlattening, tolerance, sparse, SemiMinor)[[1]] if (! sparse) { ret[ret < 0] = NA # invalid/incalculable units(ret) = units(p$SemiMajor) ret } else { if (is.null(id <- row.names(x))) id = as.character(seq_along(st_geometry(x))) structure(ret, predicate = "st_is_within_distance", region.id = id, ncol = length(st_geometry(y)), class = "sgbp") } } lwgeom/R/valid.R0000644000176200001440000000035013773172540013176 0ustar liggesusers#' Make an invalid geometry valid #' #' Make an invalid geometry valid #' @param x object of class \code{sfc} #' @export lwgeom_make_valid = function(x) { stopifnot(inherits(x, "sfc")) st_sfc(CPL_make_valid(x), crs = st_crs(x)) } lwgeom/R/RcppExports.R0000644000176200001440000000702514406665625014403 0ustar liggesusers# Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 CPL_geodetic_area <- function(sfc, semi_major, inv_flattening) { .Call('_lwgeom_CPL_geodetic_area', PACKAGE = 'lwgeom', sfc, semi_major, inv_flattening) } CPL_geodetic_length <- function(sfc, semi_major, inv_flattening) { .Call('_lwgeom_CPL_geodetic_length', PACKAGE = 'lwgeom', sfc, semi_major, inv_flattening) } CPL_geodetic_azimuth <- function(sfc, semi_major, inv_flattening) { .Call('_lwgeom_CPL_geodetic_azimuth', PACKAGE = 'lwgeom', sfc, semi_major, inv_flattening) } CPL_geodetic_segmentize <- function(sfc, max_seg_length) { .Call('_lwgeom_CPL_geodetic_segmentize', PACKAGE = 'lwgeom', sfc, max_seg_length) } CPL_geodetic_covers <- function(sfc1, sfc2) { .Call('_lwgeom_CPL_geodetic_covers', PACKAGE = 'lwgeom', sfc1, sfc2) } CPL_geodetic_distance <- function(sfc1, sfc2, semi_major, inv_flattening, tolerance, sparse, semi_minor = -1.0) { .Call('_lwgeom_CPL_geodetic_distance', PACKAGE = 'lwgeom', sfc1, sfc2, semi_major, inv_flattening, tolerance, sparse, semi_minor) } CPL_geos_version <- function(b = FALSE) { .Call('_lwgeom_CPL_geos_version', PACKAGE = 'lwgeom', b) } CPL_init_lwgeom <- function(l) { .Call('_lwgeom_CPL_init_lwgeom', PACKAGE = 'lwgeom', l) } CPL_lwgeom_version <- function(b = FALSE) { .Call('_lwgeom_CPL_lwgeom_version', PACKAGE = 'lwgeom', b) } CPL_sfc_from_twkb <- function(twkb) { .Call('_lwgeom_CPL_sfc_from_twkb', PACKAGE = 'lwgeom', twkb) } CPL_make_valid <- function(sfc) { .Call('_lwgeom_CPL_make_valid', PACKAGE = 'lwgeom', sfc) } CPL_split <- function(sfc, blade) { .Call('_lwgeom_CPL_split', PACKAGE = 'lwgeom', sfc, blade) } CPL_wrap_x <- function(sfc, wrap, move) { .Call('_lwgeom_CPL_wrap_x', PACKAGE = 'lwgeom', sfc, wrap, move) } CPL_geohash <- function(sfc, prec) { .Call('_lwgeom_CPL_geohash', PACKAGE = 'lwgeom', sfc, prec) } CPL_lwgeom_transform <- function(sfc, p4s) { .Call('_lwgeom_CPL_lwgeom_transform', PACKAGE = 'lwgeom', sfc, p4s) } CPL_minimum_bounding_circle <- function(sfc) { .Call('_lwgeom_CPL_minimum_bounding_circle', PACKAGE = 'lwgeom', sfc) } CPL_subdivide <- function(sfc, max_vertices = 256L) { .Call('_lwgeom_CPL_subdivide', PACKAGE = 'lwgeom', sfc, max_vertices) } CPL_snap_to_grid <- function(sfc, origin, size) { .Call('_lwgeom_CPL_snap_to_grid', PACKAGE = 'lwgeom', sfc, origin, size) } CPL_perimeter <- function(sfc, do2d = FALSE) { .Call('_lwgeom_CPL_perimeter', PACKAGE = 'lwgeom', sfc, do2d) } CPL_is_polygon_cw <- function(sfc) { .Call('_lwgeom_CPL_is_polygon_cw', PACKAGE = 'lwgeom', sfc) } CPL_force_polygon_cw <- function(sfc) { .Call('_lwgeom_CPL_force_polygon_cw', PACKAGE = 'lwgeom', sfc) } CPL_startpoint <- function(sfc) { .Call('_lwgeom_CPL_startpoint', PACKAGE = 'lwgeom', sfc) } CPL_endpoint <- function(sfc) { .Call('_lwgeom_CPL_endpoint', PACKAGE = 'lwgeom', sfc) } CPL_sfc_to_wkt <- function(sfc, precision) { .Call('_lwgeom_CPL_sfc_to_wkt', PACKAGE = 'lwgeom', sfc, precision) } CPL_proj_version <- function(b = FALSE) { .Call('_lwgeom_CPL_proj_version', PACKAGE = 'lwgeom', b) } CPL_use_proj4_init_rules <- function(v) { .Call('_lwgeom_CPL_use_proj4_init_rules', PACKAGE = 'lwgeom', v) } CPL_set_data_dir <- function(data_dir) { .Call('_lwgeom_CPL_set_data_dir', PACKAGE = 'lwgeom', data_dir) } CPL_linesubstring <- function(sfc, from, to, tolerance = 0.0) { .Call('_lwgeom_CPL_linesubstring', PACKAGE = 'lwgeom', sfc, from, to, tolerance) } lwgeom/R/init.R0000644000176200001440000000236513773172540013052 0ustar liggesusers#' @importFrom Rcpp evalCpp #' @import sf #' @importFrom utils tail #' @importFrom units set_units as_units #' @useDynLib lwgeom NULL .onLoad = function(libname, pkgname) { # SET PROJ.db path: if (file.exists(system.file("proj/nad.lst", package = "sf")[1])) { # we are on Windows: prj = system.file("proj", package = "sf")[1] CPL_set_data_dir(prj) # if existing, uses C API to set path CPL_use_proj4_init_rules(1L) } } .onAttach = function(libname, pkgname) { esv = lwgeom_extSoftVersion() m = paste0("Linking to liblwgeom ", esv["lwgeom"], ", GEOS ", esv["GEOS"], ", PROJ ", esv["proj.4"]) CPL_init_lwgeom(NA_character_) packageStartupMessage(m) sf = sf_extSoftVersion() if (sf["GEOS"] != esv["GEOS"]) warning(paste("GEOS versions differ: lwgeom has", esv["GEOS"], "sf has", sf["GEOS"])) if (sf["proj.4"] != esv["proj.4"]) warning(paste("PROJ versions differ: lwgeom has", esv["proj.4"], "sf has", sf["proj.4"])) } #' Provide the external dependencies versions of the libraries linked to sf #' #' Provide the external dependencies versions of the libraries linked to sf #' @export lwgeom_extSoftVersion = function() { structure(c(CPL_lwgeom_version(), CPL_geos_version(), CPL_proj_version()), names = c("lwgeom", "GEOS", "proj.4")) } lwgeom/R/perimeter.R0000644000176200001440000000143113773172540014074 0ustar liggesusers#' compute perimeter from polygons or other geometries #' #' @name perimeter #' @param x object of class \code{sf}, \code{sfc} or \code{sfg} #' @return numerical vector with perimeter for each feature (geometry), with unit of measure when possible #' @export st_perimeter = function(x) { if (isTRUE(st_is_longlat(x))) stop("for perimeter of longlat geometry, cast to LINESTRING and use st_length") # nocov ret = CPL_perimeter(st_geometry(x), FALSE) units(ret) = st_crs(x, parameters=TRUE)$ud_unit ret } #' @export #' @name perimeter st_perimeter_2d = function(x) { if (isTRUE(st_is_longlat(x))) stop("for perimeter of longlat geometry, cast to LINESTRING and use st_length") # nocov ret = CPL_perimeter(st_geometry(x), TRUE) units(ret) = st_crs(x, parameters=TRUE)$ud_unit ret } lwgeom/R/transform.R0000644000176200001440000000560213773172540014117 0ustar liggesusers#' Transform or convert coordinates of simple features directly with Proj.4 (bypassing GDAL) #' #' Transform or convert coordinates of simple features directly with Proj.4 (bypassing GDAL) #' #' @param x object of class sf, sfc or sfg #' @param crs character; target CRS, or, in case of a length 2 character vector, source and target CRS #' @param ... ignored #' @details Transforms coordinates of object to new projection, using PROJ directly rather than the GDAL API used by \link[sf]{st_transform}. #' #' If \code{crs} is a single CRS, it forms the target CRS, and in that case the source CRS is obtained as \code{st_crs(x)}. Since this presumes that the source CRS is accepted by GDAL (which is not always the case), a second option is to specify the source and target CRS as two proj4strings in argument \code{crs}. In the latter case, \code{st_crs(x)} is ignored and may well be \code{NA}. #' @examples #' library(sf) #' p1 = st_point(c(7,52)) #' p2 = st_point(c(-30,20)) #' sfc = st_sfc(p1, p2, crs = 4326) #' sfc #' st_transform_proj(sfc, "+proj=wintri") #' @export st_transform_proj = function(x, crs, ...) UseMethod("st_transform_proj") #' @name st_transform_proj #' @export st_transform_proj.sfc = function(x, crs, ...) { if (inherits(crs, "crs")) crs = if (sf_extSoftVersion()["proj.4"] >= "6.0.0") crs$wkt else crs$proj4string if (length(crs) == 1) # only target CRS given: get source crs crs = if (sf_extSoftVersion()["proj.4"] >= "6.0.0") c(st_crs(x)$wkt, crs) # c(input, output) else c(st_crs(x)$proj4string, crs) # c(input, output) stopifnot(is.character(crs), length(crs) == 2) ret = CPL_lwgeom_transform(x, crs) ret = try(st_sfc(ret, crs = crs[2])) if (inherits(ret, "try-error")) { warning("CRS of returned object is NA, since the target CRS is not recognized by GDAL") st_sfc(ret, crs = NA_crs_) } else ret } #' @name st_transform_proj #' @export #' @examples #' library(sf) #' nc = st_read(system.file("shape/nc.shp", package="sf")) #' st_transform_proj(nc[1,], "+proj=wintri +over") st_transform_proj.sf = function(x, crs, ...) { x[[ attr(x, "sf_column") ]] = st_transform_proj(st_geometry(x), crs, ...) x } #' @name st_transform_proj #' @export #' @details The \code{st_transform_proj} method for \code{sfg} objects assumes that the CRS of the object is available as an attribute of that name. #' @examples #' st_transform_proj(structure(p1, proj4string = "+init=epsg:4326"), "+init=epsg:3857") st_transform_proj.sfg = function(x, crs, ...) { if (missing(crs)) stop("argument crs cannot be missing") # nocov if (length(crs) == 1) { if (is.null(attr(x, "proj4string"))) stop("x does not have a proj4string attribute") # nocov if (!is.character(attr(x, "proj4string"))) stop("proj4string attribute should be a character string") # nocov crs = c(attr(x, "proj4string"), crs) } structure(st_transform_proj(st_sfc(x), crs, ...)[[1]], proj4string = tail(crs, 1)) } lwgeom/R/geohash.R0000644000176200001440000000141013773172540013513 0ustar liggesusers#' compute geohash from (average) coordinates #' #' @param x object of class \code{sf}, \code{sfc} or \code{sfg} #' @param precision integer; precision (length) of geohash returned. From the liblwgeom source: ``where the precision is non-positive, a precision based on the bounds of the feature. Big features have loose precision. Small features have tight precision.'' #' @export #' @details see \url{http://geohash.org/} or \url{https://en.wikipedia.org/wiki/Geohash}. #' @return character vector with geohashes #' @examples #' library(sf) #' lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 2) #' lwgeom::st_geohash(st_sfc(st_point(c(1.5,3.5)), st_point(c(0,90))), 10) st_geohash = function(x, precision = 0) { CPL_geohash(st_geometry(x), precision) } lwgeom/R/startpoint.R0000644000176200001440000000161713773172540014315 0ustar liggesusers#' Return the start and end points from lines #' #' @param x line of class \code{sf}, \code{sfc} or \code{sfg} #' @export #' @details see \url{https://postgis.net/docs/ST_StartPoint.html} and \url{https://postgis.net/docs/ST_EndPoint.html}. #' @return \code{sf} object representing start and end points #' @examples #' library(sf) #' m = matrix(c(0, 1, 2, 0, 1, 4), ncol = 2) #' l = st_sfc(st_linestring(m)) #' lwgeom::st_startpoint(l) #' lwgeom::st_endpoint(l) #' l2 = st_sfc(st_linestring(m), st_linestring(m[3:1, ])) #' lwgeom::st_startpoint(l2) #' lwgeom::st_endpoint(l2) st_startpoint = function(x) { m <- CPL_startpoint(st_geometry(x)) st_sfc(lapply(seq_len(nrow(m)), function(i) st_point(m[i,])), crs = st_crs(x)) } #' @rdname st_startpoint #' @export st_endpoint = function(x) { m <- CPL_endpoint(st_geometry(x)) st_sfc(lapply(seq_len(nrow(m)), function(i) st_point(m[i,])), crs = st_crs(x)) } lwgeom/R/bearing.R0000644000176200001440000000103213773172540013504 0ustar liggesusers#' compute azimuth between sequence of points #' #' compute azimuth between sequence of points #' @param x object of class \code{sf}, \code{sfc} or \code{sfg} #' @export #' @examples #' library(sf) #' p = st_sfc(st_point(c(7,52)), st_point(c(8,53)), crs = 4326) #' st_geod_azimuth(p) st_geod_azimuth = function(x) { stopifnot(st_is_longlat(x)) stopifnot(all(st_is(x, "POINT"))) p = st_crs(st_geometry(x), parameters = TRUE) ret = CPL_geodetic_azimuth(st_geometry(x), p$SemiMajor, p$InvFlattening) units(ret) = as_units("rad") ret } lwgeom/R/twkb.R0000644000176200001440000000075513773172540013057 0ustar liggesusers#' create sfc object from tiny well-known binary (twkb) #' #' create sfc object from tiny well-known binary (twkb) #' @param x list with raw vectors, of class \code{TWKB} #' @param ... ignored #' @seealso https://github.com/TWKB/Specification/blob/master/twkb.md #' @examples #' l = structure(list(as.raw(c(0x02, 0x00, 0x02, 0x02, 0x02, 0x08, 0x08))), class = "TWKB") #' library(sf) # load generic #' st_as_sfc(l) #' @export st_as_sfc.TWKB = function(x, ...) { st_sfc(CPL_sfc_from_twkb(x)) } lwgeom/R/bounding_circle.R0000644000176200001440000000370513773172540015234 0ustar liggesusers#' Generate the minimum bounding circle #' @name bounding_circle #' @param x object of class \code{sfg}, \code{sfg} or \code{sf} #' @param nQuadSegs number of segments per quadrant (passed to \code{st_buffer}) #' @return Object of the same class as \code{x} #' @details \code{st_minimum_bounding_circle} uses the \code{lwgeom_calculate_mbc} method also used by the PostGIS command \code{ST_MinimumBoundingCircle}. #' @examples #' library(sf) #' #' x = st_multipoint(matrix(c(0,1,0,1),2,2)) #' y = st_multipoint(matrix(c(0,0,1,0,1,1),3,2)) #' #' mbcx = st_minimum_bounding_circle(x) #' mbcy = st_minimum_bounding_circle(y) #' #' if (.Platform$OS.type != "windows") { #' plot(mbcx, axes=TRUE); plot(x, add=TRUE) #' plot(mbcy, axes=TRUE); plot(y, add=TRUE) #' } #' #' nc = st_read(system.file("gpkg/nc.gpkg", package="sf")) #' state = st_union(st_geometry(nc)) #' #' if (.Platform$OS.type != "windows") { #' plot(st_minimum_bounding_circle(state), asp=1) #' plot(state, add=TRUE) #' } #' #' @export st_minimum_bounding_circle = function(x, nQuadSegs = 30) UseMethod("st_minimum_bounding_circle", x) generate_circles = function(geom, nQuadSegs = 30) { stopifnot(inherits(geom,"sfc")) circles = CPL_minimum_bounding_circle(geom) if (any(is.nan(unlist(circles)))) stop("NaN values returned by lwgeom's lwgeom_calculate_mbc.") #nocov mapply( function(xy, r, nQuadSegs) { st_buffer(st_point(xy), r, nQuadSegs) }, circles[["center"]], circles[["radius"]], MoreArgs = list(nQuadSegs = nQuadSegs), SIMPLIFY = FALSE ) } #' @export st_minimum_bounding_circle.sfg = function(x, nQuadSegs = 30) { st_minimum_bounding_circle(st_geometry(x), nQuadSegs)[[1]] } #' @export st_minimum_bounding_circle.sfc = function(x, nQuadSegs = 30) { st_sfc(generate_circles(x, nQuadSegs), crs = st_crs(x)) } #' @export st_minimum_bounding_circle.sf = function(x, nQuadSegs = 30) { st_set_geometry(x, st_minimum_bounding_circle(st_geometry(x), nQuadSegs)) } lwgeom/NEWS.md0000644000176200001440000000454714360536540012662 0ustar liggesusers# version 0.2-12 # version 0.2-11 * replace `sprintf()` instances with `snprintf()` # version 0.2-10 * fix -Wstrict-prototypes warnings # version 0.2-9 * fix formatting issues for long long int # version 0.2-8 * remove PROBLEM ... ERROR constructs from C code # version 0.2-5 * GEOS requirement lowered to 3.5.0, which also seems to work; #59. # version 0.2-4 * require sf >= 0.9-3, and use C API PROJ path setting (to work on CRAN windows binaries) * update to new GEOS (3.8.0) and PROJ (6.3.1) versions for CRAN windows binary builds * require GEOS 3.6.0 (required by PostGIS 3.0.0), and add check to configure # version 0.2-3 * fix configure script to work with ubuntu/bionic and PROJ 4.9.3; #28 # version 0.2-3 * fix configure script to work with PROJ 5.x versions # version 0.2-2 * adjust to sf >= 0.9-0 new crs representation * use `st_make_valid` generic from package sf; https://github.com/r-spatial/sf/issues/1300 # version 0.2-1 * fix PROJ 5.x installation issue (has proj.h, but shouldn't use it) # version 0.2-0 * export `lwgeom_make_valid`, to gradually move `st_make_valid` from `lwgeom` to `sf`; https://github.com/r-spatial/sf/issues/989 * constrain argument `crs` in `st_transform_proj` to take one or two character strings * update to POSTGIS 3.0.0 liblwgeom version * update to modern PROJ, use proj.h when available # version 0.1-5 * check for user interrupts on `st_geod_distance`, #29 by Dan Baston * add `st_astext` for fast WKT writing, #25 by Etienne Racine * add `st_is_polygon_cw`, #21 by Andy Teucher @ateucher; add Andy Teucher to contributors * add `st_perimeter` and `st_perimeter_2d` functions to compute the length measurement of the boundary of a surface. * allow `st_transform_proj` to take two proj4 strings as crs, as `c(input_p4s, output_p4s)`, ignoring the CRS of x # version 0.1-4 * tries to fix the CRAN error for r-release-osx (datum files missing in sf; removed test) # version 0.1-3 * add `st_geod_covered_by` binary geometry predicate # version 0.1-2 * try to fix OSX compile on CRAN, tuning configure.ac # version 0.1-1 * add `st_length` * attempt to fix Solaris and OSX * report proj.4 and GEOS versions on startup, and on `lwgeom_extSoftwareVersions`; #10 * add minimum bounding circle, by @rundel; #7 * add `st_subdivide`, see https://github.com/r-spatial/sf/issues/597 # version 0.1-0 * first CRAN submission lwgeom/MD50000644000176200001440000002274314432644332012071 0ustar liggesusers69893414e39a0017afab7effa976ded1 *DESCRIPTION fd0e6f65230302c9fca3e79041608392 *NAMESPACE 2105125024071cf342f35ad566f0943f *NEWS.md c9d5e979f06c23fc39638373c8744e51 *R/RcppExports.R df1ca2557a4c3a587772e671209b91b4 *R/bearing.R 217897cfe81dee9673ea232801b3c184 *R/bounding_circle.R 4f65efddbfce0fb7253cdf451f703af0 *R/clockwise.R c2247a1bfa255d1246d1771ea06a3b39 *R/geod.R 2e84fe4bed4d9f25ab9c51f67fd85f9d *R/geohash.R a078fc6698e93ead1f9b5df8ac381446 *R/init.R b2f66749cac540d6c40155306cb30476 *R/perimeter.R 6d4377d348be86eee2c2bd4e25e44460 *R/snap_to_grid.R 267d22d9101596587c082a351ea4573d *R/split.R 0cd3914a53f8ae5a2b5a0da99d8911a1 *R/startpoint.R 14ed66ee62d7b77ef46fa221f6ba4639 *R/subdivide.R 4e732a67a6053918908bf6ffbfd5492a *R/transform.R 911d4da2251a1abb14706993801647b7 *R/twkb.R 9b19b4ebe53d8a5eddf697645bfdfab6 *R/valid.R 9587901a41fee148f8d73007a4f1fdb4 *R/wkt.R 6495c01ca83bd1fbcf31c7776d62ef5c *R/wrap_x.R e217fdeb3f619171e0a751892bbf2145 *cleanup 501febc7e815948f99f4752a8fb8dfa2 *configure 1ab1c87041fc53d972d69cce911b233b *configure.ac 58aecbd7c50ef5305813f300d598bae9 *inst/COPYRIGHTS a7dbfb5d2e427de80b81d650f46df1d0 *man/bounding_circle.Rd 36ba2aaeab3b50685e62e54f55b3a892 *man/geod.Rd 413ad9291be1257922796c4b52644b2d *man/lwgeom_extSoftVersion.Rd 5195299939c960b893cb7b47521991a2 *man/lwgeom_make_valid.Rd 7fade9cafe93064bce4f6e406110bd2f *man/perimeter.Rd 3dc282cb5ec2ab4f6510715ddae99f61 *man/st_as_sfc.TWKB.Rd e9f05605b5c31bd9b0c4e26b5ea91f95 *man/st_astext.Rd 64aced576d33ba913a62a750bc673e98 *man/st_force_polygon_cw.Rd 5e398d27769649b35439876b88c62876 *man/st_geod_azimuth.Rd 9eb0a0af04746dd4a318a8a87236a4b3 *man/st_geohash.Rd 98dbd2d3ced4f282641d8e07b8a63cd6 *man/st_is_polygon_cw.Rd 4f3547929d58a06291555e60017a2203 *man/st_linesubstring.Rd b11adce7a9842477837efff4370767da *man/st_snap_to_grid.Rd 33ca6084343b9554861a96868d8a4711 *man/st_split.Rd 80abcf275b990697cdca0e68c739bbdd *man/st_startpoint.Rd d97760746b591e99c1fbd65a39fca882 *man/st_subdivide.Rd 5cad3c8d25b2949ac2be18b2dfe0ca45 *man/st_transform_proj.Rd 2beed75134b26f275e29e3be181d8ec3 *man/st_wrap_x.Rd 6f9175b225390c0a8a4e9cd8fc3c2497 *src/DIFFS 745c8fdbf211ff5eae9f5bcd182b3d75 *src/Makevars.in ce893d74ba270844c70dc3e9a4229c84 *src/Makevars.ucrt b5e6ec42436bd4d2ae55ee06d3a51d88 *src/Makevars.win 8c3627d65fcbf4690e58164d0aa8047e *src/README bd15c9082014cfdf8f8f17aa693738af *src/RcppExports.cpp e92cd9a388d5d99c08f8abb549d3b52f *src/geodetic.cpp bc67cda97361b67573250f62c3d258df *src/geos.cpp abeb33ab3c689550cc1e884dc3f311fb *src/io.cpp 981ea57326356b71912ee57c0af73f90 *src/liblwgeom/DIFFS 155eaec9d0ba6b79cc3c7a55bcbc2b50 *src/liblwgeom/bytebuffer.c d838def5bb4928bb5fbcb24d4609d8c3 *src/liblwgeom/bytebuffer.h cc6df78ae63ace47ecb421c3c52acb54 *src/liblwgeom/effectivearea.c 642a0c9c88cc329c3ae4a13c061a0354 *src/liblwgeom/effectivearea.h 31fd2327ab196e3dd8c65718f12f6ae8 *src/liblwgeom/gbox.c 0d42e7689977cc1348c9d937bcc5cb55 *src/liblwgeom/gserialized.c cb13abf5cf0b4a57d752e650f0006c61 *src/liblwgeom/gserialized.h 44413c8b3e24afbc997fcf13970d6bd5 *src/liblwgeom/gserialized1.c 4c5254742c13d9fbb388c8942e4803d1 *src/liblwgeom/gserialized1.h bdf40c320e486a638b95eac5a845f0d4 *src/liblwgeom/gserialized2.c 8e90b7804fe2d46df9816be5f20e21cd *src/liblwgeom/gserialized2.h 4d895d23ba57e76667a6bdfcb62fab91 *src/liblwgeom/liblwgeom.h 589007bc71260fb133aede777239b5d9 *src/liblwgeom/liblwgeom_internal.h 5ecf5d2e03dd5ba6308fc32909330df3 *src/liblwgeom/liblwgeom_topo.h a93c474ccda9c60e77d5ab238eff923b *src/liblwgeom/liblwgeom_topo_internal.h 5a2bf8d95d912df4232e2ebe9a00452f *src/liblwgeom/lookup3.c 5d298ac74cce6b14322cb435eaeee44f *src/liblwgeom/lwalgorithm.c cf9d8184e0422bf5d0d262ede610c720 *src/liblwgeom/lwboundingcircle.c 14e04da1b3c6f69eaa8af1844ac5c449 *src/liblwgeom/lwchaikins.c 64e61747e0e98c1c6532227363842974 *src/liblwgeom/lwcircstring.c 1b2e0b9968c1816d8eccd8eaa26466c4 *src/liblwgeom/lwcollection.c 0a8248522833287d07ae693b7f4604b2 *src/liblwgeom/lwcompound.c 79e34a9d1f2d0f26e085363380d1d245 *src/liblwgeom/lwcurvepoly.c 83a2084a50a3519183376fb45d4ec841 *src/liblwgeom/lwgeodetic.c 1c95ba11707400ecc06ecfee2323a283 *src/liblwgeom/lwgeodetic.h 965fd0bd88b400086452a0651fea4527 *src/liblwgeom/lwgeodetic_tree.c 16b6dcc310cd9642d8b96b1f5b268775 *src/liblwgeom/lwgeodetic_tree.h 25f0d2bf912291821e07ba0e88a370fd *src/liblwgeom/lwgeom.c 87a0583770c3353fda5544b5e230cb9e *src/liblwgeom/lwgeom_api.c 7d5a37cb431ffc8c8b2d4fe5ba85a982 *src/liblwgeom/lwgeom_debug.c ffa9cc8edb7ae6cc36de11e0b51c4bc1 *src/liblwgeom/lwgeom_geos.c df0ca13d3c4e7e50042a6565b92dfcac *src/liblwgeom/lwgeom_geos.h 4d397ed98e808db92e78ffc0ab4a42ad *src/liblwgeom/lwgeom_geos_clean.c 6f05b2721c2b2d7de325b48fe8b8ce3e *src/liblwgeom/lwgeom_geos_cluster.c 52b130dfa56d47785636f0cddc1741eb *src/liblwgeom/lwgeom_geos_node.c e74701b2e828f19500dc8d04848b0d60 *src/liblwgeom/lwgeom_geos_split.c 8521108448b4581b5f0d76c7c7eed2d5 *src/liblwgeom/lwgeom_log.h ef88b50f0e223320b6538684a3931693 *src/liblwgeom/lwgeom_median.c 124b6d9404697b88e9d2d5d6a704b074 *src/liblwgeom/lwgeom_topo.c f90ba2426040c8d2cc853ab65bc7333a *src/liblwgeom/lwgeom_transform.c 151b2f07e3fe2e3276b97b26fcc2d432 *src/liblwgeom/lwgeom_wrapx.c 694637af8f4cf87f7581881bf0c08d29 *src/liblwgeom/lwhomogenize.c a7e82c8e6c0c9330322b92e5a130ea11 *src/liblwgeom/lwin_encoded_polyline.c 9a6e6f81f744b2c0aadbcacdac70d50d *src/liblwgeom/lwin_twkb.c 6e115d6f8b6fe3cac507b58d3f5ddf31 *src/liblwgeom/lwin_wkb.c 93e9bd1ac615cd42b0cc7453fe03877d *src/liblwgeom/lwin_wkt.c 20e74ffc34f441646c570fa058aae492 *src/liblwgeom/lwin_wkt.h a958dff69ac943078358c946e3d16cef *src/liblwgeom/lwin_wkt_lex.c a33e2e3106decb5c7c209ded26215fea *src/liblwgeom/lwin_wkt_parse.c 1197c34e06395441720ce62b5369927e *src/liblwgeom/lwin_wkt_parse.h 67669685ef528d7a2297a0b0e854bb64 *src/liblwgeom/lwinline.h 02cfe3a3684bf4caa4670a83e1b6da9d *src/liblwgeom/lwiterator.c abacd4026eb9d57c971daa28d924557f *src/liblwgeom/lwkmeans.c 645e437c6abc493a1c1ca5dc11274d3b *src/liblwgeom/lwline.c 32cea52d9a9d8572981bf605bc5d3293 *src/liblwgeom/lwlinearreferencing.c 5435f3fdb4b14f8da77224a4cf39ae19 *src/liblwgeom/lwmcurve.c 0732acbac3b89b44885424d20e4517fb *src/liblwgeom/lwmline.c 4b2e2483cd9c9a28d92cdcaf048d6612 *src/liblwgeom/lwmpoint.c f4f4bd93540e67c50bb3d6c69c7d099e *src/liblwgeom/lwmpoly.c 47f8b9f1228ca46db8f57d39546ed339 *src/liblwgeom/lwmsurface.c 898ef643057a4359ff4b82e3b6efdda0 *src/liblwgeom/lwmval.c c7661ce52859a3f245ad35b42ba197d4 *src/liblwgeom/lwout_encoded_polyline.c aa470eff2472b8ab93de34194eda20a1 *src/liblwgeom/lwout_geojson.c 2e8a83cc7f1948f4e41535486e8d140e *src/liblwgeom/lwout_gml.c 2d06fbce786ecf650d3d0e6038887c2a *src/liblwgeom/lwout_kml.c 0317510cd7e16b9396293fbdacfc31be *src/liblwgeom/lwout_svg.c 01c06537a6d836f8e01e1db4e0a23884 *src/liblwgeom/lwout_twkb.c ddfe828c6d1ae3c3626e05cfbeca4a13 *src/liblwgeom/lwout_twkb.h d762325b916cbb51c7b2b5bb053981ca *src/liblwgeom/lwout_wkb.c 302cd0c42bef9540366d83d318a907d9 *src/liblwgeom/lwout_wkt.c 1d2c71f5c16f6d092e68bf913fa38673 *src/liblwgeom/lwout_x3d.c 708f6fc5ce944d07742da97ef4c681de *src/liblwgeom/lwout_x3d.h 731e6d42823ddf61287305651719a4f9 *src/liblwgeom/lwpoint.c 0e93f65de4ad6ba33178dcc8c902d446 *src/liblwgeom/lwpoly.c 504175f776ba21506533711240f262f6 *src/liblwgeom/lwprint.c e0fc6e0a51a41633619be95fbf066177 *src/liblwgeom/lwpsurface.c dca4d980b703ff77bb191302d27af882 *src/liblwgeom/lwrandom.c a6db8bd40f93e1a97aef1dc02251553c *src/liblwgeom/lwrandom.h 91a01b8cfb838983802a5a011ec68148 *src/liblwgeom/lwspheroid.c 95dc7e98da182806ee515b56958a2186 *src/liblwgeom/lwstroke.c f58e1f7fb524b747e145e14c35277991 *src/liblwgeom/lwtin.c 883caa0c99e5a13e1fb20326b8689a3e *src/liblwgeom/lwtriangle.c aec8b8402158bdf59e0fcff40364e981 *src/liblwgeom/lwunionfind.c 586f44185eaaa83e02d154b7b12f7764 *src/liblwgeom/lwunionfind.h db0a9b07390f1d854fe86826892de7e2 *src/liblwgeom/lwutil.c a23ef1572b23258b7ef63b2597fa6739 *src/liblwgeom/measures.c fea91c101e932800b38c3cea5b2b7bb6 *src/liblwgeom/measures.h 79ed4c7b114de65b297051840b52819a *src/liblwgeom/measures3d.c c193faf0960c009d99e5ff055c4f5fd4 *src/liblwgeom/measures3d.h fe71afc62ef2bafa7efc891c0d1071a0 *src/liblwgeom/ptarray.c 4818b8bb66f295bb1c7fcd44854cb300 *src/liblwgeom/stringbuffer.c 252be5c801d58d73281486290fb291f6 *src/liblwgeom/stringbuffer.h 995cd9c6bac6e31459f4da7ba9dd82be *src/liblwgeom/varint.c 09d38c8b48efb683b87dd1ee09d1ba49 *src/liblwgeom/varint.h 850435ebccdf86b11760af3e2ef23309 *src/lwgeom.cpp a5bcf547205b549793cde6c1be6e8f12 *src/lwgeom.h 3085f85a05b12cf24aeff5f8721997d6 *src/postgis_config.h f5a3e3a5e36fbcd323f5d03ed7ec6786 *src/postgis_config.h.in 9b81947fbe464699b85cc9cae784e6af *src/postgis_config.win 62bfb3f17903dbba24467b092b8e2f04 *src/postgis_svn_revision.h 473818526fed36b01bb80bf1e8bcaabf *src/proj.cpp bdc2019f2c6f101e52d2298f9f131eb4 *src/sub.cpp 5ce641782adeccdaeff59d10ee5c6260 *tests/azimuth.R bd53da1ddc0b87da4a1bc944194a8e57 *tests/azimuth.Rout.save 3c7494be4485a5cb9eac533b2a206f85 *tests/dist.R 1e42ba25721c272887c9447cae3def41 *tests/dist.Rout.save 65b26e630db2ac843824de12abdd975d *tests/geod.R adc5c7d0052bbe70835c5f3890b3afcd *tests/geod.Rout.save 3fa981ab562ea303b2dc48f32b4e0205 *tests/perimeter.R cd57b1b6cd6075754b428eca416ca142 *tests/perimeter.Rout.save 8fd45a4aebfb491127d6f52e33f1ed59 *tests/testthat.R b8ec18468ec83564c97954a556ee201c *tests/testthat.Rout.save ea1079926e5754effd134cd211bd9263 *tests/testthat/test-as_text.R 43a596e15ba4e7ba3502cb97f4785776 *tests/testthat/test-clockwise.R d657e3852379da365666fb3c73fbc20f *tests/testthat/test_lwgeom.R 1b0e74035b1e3f6fe65cd934b99f9fd7 *tests/twkb.R 784f7c3e5ae412ab5b132d6f26bf329a *tests/twkb.Rout.save fa54314ef0767263d67b30f06befcfb1 *tools/winlibs.R lwgeom/inst/0000755000176200001440000000000013773172540012532 5ustar liggesuserslwgeom/inst/COPYRIGHTS0000644000176200001440000000177113773172540014156 0ustar liggesusersCopyright holders of the liblwgeom library (files in src/liblwgeom): Copyright (C) 2004-2015 Sandro Santilli Copyright (C) 2008-2011 Paul Ramsey Copyright (C) 2008 Mark Cave-Ayland Copyright 2015 Nicklas Avén Copyright 2017 Darafei Praliaskouski Copyright 2001-2006 Refractions Research Inc. Copyright 2015 Daniel Baston Copyright 2009 David Skea Copyright 2010 Olivier Courtin Copyright 2011 Kashif Rasul Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. Copyright 2010-2012 Oslandia Copyright 2006 Corporacion Autonoma Regional de Santander Originally written by: Klaus Förster Refactored by: Olivier Courtin (Camptocamp) Copyright 2011-2016 Arrival 3D, Regina Obe Copyright (C) 2006 Mark Leslie Copyright 2002 Thamer Alharbash lwgeom/cleanup0000755000176200001440000000025114432625425013126 0ustar liggesusers#!/bin/sh rm -fr src/Makevars config.log config.status autom4te.cache src/*.o src/*.so src/liblwgeom/*.o */Rplots.pdf proj_conf_test proj_conf_test.c proj_conf_test.cpp lwgeom/configure0000755000176200001440000044027214432625425013473 0ustar liggesusers#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='' PACKAGE_TARNAME='' PACKAGE_VERSION='' PACKAGE_STRING='' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_unique_file="src/lwgeom.cpp" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS PKG_LIBS OBJECTS GEOS_LIBS PKG_CPPFLAGS GEOS_CONFIG POSTGIS_PROJ_VERSION PROJ_LIBS OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC PROJ_CPPFLAGS target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_proj_include with_proj_api with_proj_lib with_proj_share with_geos_config ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-proj-include=DIR location of proj header files --with-proj-api=yes/no use the deprecated proj_api.h even when PROJ 6 is available; default no --with-proj-lib=LIB_PATH the location of proj libraries --with-proj-share=SHARE_PATH the location of proj metadata files --with-geos-config=GEOS_CONFIG the location of geos-config Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu : ${R_HOME=`R RHOME`} if test -z "${R_HOME}"; then echo "could not determine R_HOME" exit 1 fi RBIN="${R_HOME}/bin/R" # pick all flags for testing from R : ${CC=`"${RBIN}" CMD config CC`} : ${CXX=`"${RBIN}" CMD config CXX`} : ${CFLAGS=`"${RBIN}" CMD config CFLAGS`} : ${CPPFLAGS=`"${RBIN}" CMD config CPPFLAGS`} : ${LDFLAGS=`"${RBIN}" CMD config LDFLAGS`} # AC_SUBST([CC],["clang"]) # AC_SUBST([CXX],["clang++"]) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: CC: ${CC}" >&5 printf "%s\n" "$as_me: CC: ${CC}" >&6;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: CXX: ${CXX}" >&5 printf "%s\n" "$as_me: CXX: ${CXX}" >&6;} # # PROJ # PROJ_CONFIG="pkg-config proj" if `$PROJ_CONFIG --exists` ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: pkg-config proj exists, will use it" >&5 printf "%s\n" "$as_me: pkg-config proj exists, will use it" >&6;} proj_config_ok=yes else proj_config_ok=no fi # Check whether --with-proj-include was given. if test ${with_proj_include+y} then : withval=$with_proj_include; proj_include_path=$withval fi if test -n "$proj_include_path" ; then PROJ_CPPFLAGS="-I${proj_include_path}" else if test "${proj_config_ok}" = yes; then PROJ_INCLUDE_PATH=`${PROJ_CONFIG} --cflags` PROJ_CPPFLAGS="${PROJ_INCLUDE_PATH}" fi fi # honor PKG_xx overrides # for CPPFLAGS we will superfluously double R's flags # since we'll set PKG_CPPFLAGS with this, but that shouldn't hurt # Check whether --with-proj-api was given. if test ${with_proj_api+y} then : withval=$with_proj_api; proj_api=$withval fi PROJ6="no" PROJH="no" if test "${proj_config_ok}" = yes; then PROJ_VERSION=`${PROJ_CONFIG} --modversion` PROJV1=`echo "${PROJ_VERSION}" | cut -c 1` if test "${PROJV1}" -ge 5; then PROJ6="yes" PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DHAVE_PROJ_H" if test "${proj_api}" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: using proj_api.h even with PROJ 5/6" >&5 printf "%s\n" "$as_me: using proj_api.h even with PROJ 5/6" >&6;} PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: using proj.h." >&5 printf "%s\n" "$as_me: using proj.h." >&6;} PROJH="yes" fi fi else if test "${PROJH}" = no ; then PROJH=yes ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in proj.h do : ac_fn_c_check_header_compile "$LINENO" "proj.h" "ac_cv_header_proj_h" "$ac_includes_default" if test "x$ac_cv_header_proj_h" = xyes then : printf "%s\n" "#define HAVE_PROJ_H 1" >>confdefs.h else $as_nop PROJH=no fi done if test "${PROJH}" = yes; then PROJ6="yes" PROJ_CPPFLAGS="${PROJ_CPPFLAGS} -DHAVE_PROJ_H" fi fi fi CPPFLAGS="${INCPPFLAGS} ${PKG_CPPFLAGS} ${PROJ_CPPFLAGS}" # see https://github.com/r-spatial/lwgeom/issues/28 #if test "${PROJH}" = no #then # proj4ok=yes # AC_CHECK_HEADERS(proj_api.h,,proj4ok=no) # if test "${proj4ok}" = no; then # AC_MSG_ERROR([proj_api.h not found in standard or given locations.]) # fi #fi # dnl ditto for a library path # Check whether --with-proj-lib was given. if test ${with_proj_lib+y} then : withval=$with_proj_lib; proj_lib_path=$withval fi if test -n "$proj_lib_path" ; then PROJ_LIBS="-L${proj_lib_path} ${INPKG_LIBS} -lproj" else if test "${proj_config_ok}" = yes; then if test `uname` = "Darwin"; then PROJ_LIB_PATH=`${PROJ_CONFIG} --libs --static` else PROJ_LIB_PATH=`${PROJ_CONFIG} --libs` fi PROJ_LIBS="${PROJ_LIB_PATH} ${INPKG_LIBS}" proj_version=`${PROJ_CONFIG} --modversion` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: PROJ: ${proj_version}" >&5 printf "%s\n" "$as_me: PROJ: ${proj_version}" >&6;} else PROJ_LIBS="${PKG_LIBS} -lproj" fi fi LIBS="${PROJ_LIBS} ${INLIBS} ${PKG_LIBS}" if test "${PROJH}" = no; then proj4ok=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pj_init_plus in -lproj" >&5 printf %s "checking for pj_init_plus in -lproj... " >&6; } if test ${ac_cv_lib_proj_pj_init_plus+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lproj $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pj_init_plus (); int main (void) { return pj_init_plus (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_proj_pj_init_plus=yes else $as_nop ac_cv_lib_proj_pj_init_plus=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_proj_pj_init_plus" >&5 printf "%s\n" "$ac_cv_lib_proj_pj_init_plus" >&6; } if test "x$ac_cv_lib_proj_pj_init_plus" = xyes then : printf "%s\n" "#define HAVE_LIBPROJ 1" >>confdefs.h LIBS="-lproj $LIBS" else $as_nop proj4ok=no fi if test "${proj4ok}" = no; then as_fn_error $? "libproj not found in standard or given locations." "$LINENO" 5 fi cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { printf("%d\n", PJ_VERSION); exit(0); } _EOCONF else cat > proj_conf_test.cpp <<_EOCONF #include #include #include int main(void) { proj_context_create(); exit(0); } _EOCONF #AC_CHECK_LIB(proj,proj_context_create,,proj6ok=no) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PROJ: checking whether PROJ and sqlite3 are available for linking:" >&5 printf %s "checking PROJ: checking whether PROJ and sqlite3 are available for linking:... " >&6; } ${CXX} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.cpp ${LIBS} -lsqlite3 2> errors.txt if test `echo $?` -ne 0 ; then proj6ok=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else proj6ok=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi if test "${proj6ok}" = no; then as_fn_error $? "libproj not found in standard or given locations." "$LINENO" 5 fi cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { printf("%d.%d.%d\n", PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH); exit(0); } _EOCONF fi #AC_MSG_NOTICE([PKG_LIBS: ${PKG_LIBS}]) ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} proj_version=`./proj_conf_test` # Check whether --with-proj-share was given. if test ${with_proj_share+y} then : withval=$with_proj_share; proj_share_path=$withval fi if test -n "$proj_share_path" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: PROJ_LIB: ${proj_share_path}" >&5 printf "%s\n" "$as_me: PROJ_LIB: ${proj_share_path}" >&6;} fi if test ${PROJ6} = "no"; then cat > proj_conf_test.c <<_EOCONF #include #include #include #if PJ_VERSION <= 480 FILE *pj_open_lib(projCtx, const char *, const char *); #endif int main(void) { #if PJ_VERSION <= 480 FILE *fp; #else PAFile fp; #endif projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "epsg", "rb"); if (fp == NULL) exit(1); #if PJ_VERSION <= 480 fclose(fp); #else pj_ctx_fclose(ctx, fp); #endif exit(0); } _EOCONF ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test -n "$proj_share_path" ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PROJ: epsg found and readable" >&5 printf %s "checking PROJ: epsg found and readable... " >&6; } if test ${proj_share} -eq 1 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } STOP="stop" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi rm -f proj_conf_test.c proj_conf_test if test "$STOP" = "stop" ; then echo "Error: proj/epsg not found" echo "Either install missing proj support files, for example" echo "the proj-nad and proj-epsg RPMs on systems using RPMs," echo "or if installed but not autodetected, set PROJ_LIB to the" echo "correct path, and if need be use the --with-proj-share=" echo "configure argument." exit 1 fi else # proj >= 6 if test "${PROJH}" = no; then cat > proj_conf_test.c <<_EOCONF #include #include #include int main(void) { PAFile fp; projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "proj.db", "rb"); if (fp == NULL) exit(1); pj_ctx_fclose(ctx, fp); exit(0); } _EOCONF ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test -n "$proj_share_path" ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PROJ: proj.db found and readable" >&5 printf %s "checking PROJ: proj.db found and readable... " >&6; } if test ${proj_share} -eq 1 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } STOP="stop" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi rm -f proj_conf_test.c proj_conf_test if test "$STOP" = "stop" ; then echo "Error: proj/proj.db not found" echo "Either install missing proj support files, set PROJ_LIB to the" echo "correct path, and if need be use the --with-proj-share=" echo "configure argument." exit 1 fi cat > proj_conf_test.c <<_EOCONF #include #include #include #if PJ_VERSION <= 480 FILE *pj_open_lib(projCtx, const char *, const char *); #endif int main(void) { #if PJ_VERSION <= 480 FILE *fp; #else PAFile fp; #endif projCtx ctx; ctx = pj_get_default_ctx(); fp = pj_open_lib(ctx, "conus", "rb"); if (fp == NULL) exit(1); #if PJ_VERSION <= 480 fclose(fp); #else pj_ctx_fclose(ctx, fp); #endif exit(0); } _EOCONF ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} if test -n "$proj_share_path" ; then PROJ_LIB="${proj_share_path}" ./proj_conf_test proj_share=`echo $?` else ./proj_conf_test proj_share=`echo $?` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PROJ: conus found and readable" >&5 printf %s "checking PROJ: conus found and readable... " >&6; } if test ${proj_share} -eq 1 ; then WARN="warn" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi rm -f proj_conf_test.c proj_conf_test if test "$WARN" = "warn" ; then echo "Note: proj/conus not found" echo "No support available in PROJ4 for NAD grid datum transformations" echo "If required, consider re-installing from source with the contents" echo "of proj-datumgrid-1..zip from http://download.osgeo.org/proj/ in nad/." fi fi # PROJH = no fi # proj >= 6 # # POSTGIS/PROJ version: # cat > proj_conf_test.c <<_EOCONF #include #ifdef HAVE_PROJ_H #include int main(void) { printf("%d%d%d\n", PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH); return 0; } #else #include int main(void) { printf("%d\n", PJ_VERSION); return 0; } #endif _EOCONF # AC_MSG_NOTICE([PKG_LIBS: ${PKG_LIBS}]) ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o proj_conf_test proj_conf_test.c ${PROJ_LIBS} proj_version=`./proj_conf_test` POSTGIS_PROJ_VERSION=`echo $proj_version | cut -c "1,2"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: POSTGIS_PROJ_VERSION: ${POSTGIS_PROJ_VERSION}" >&5 printf "%s\n" "$as_me: POSTGIS_PROJ_VERSION: ${POSTGIS_PROJ_VERSION}" >&6;} #AC_SUBST([POSTGIS_PROJ_VERSION],["${PROJV12}"]) #AC_DEFINE_UNQUOTED([POSTGIS_PROJ_VERSION], [$POSTGIS_PROJ_VERSION], [PROJ library version]) # xxx if test ${POSTGIS_PROJ_VERSION} -lt 60 ; then PKG_CPPFLAGS="${PKG_CPPFLAGS} -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H" else PKG_CPPFLAGS="${PKG_CPPFLAGS} -DUSE_PROJ_H" fi # # GEOS: # GEOS_CONFIG="geos-config" GEOS_CONFIG_SET="no" # Check whether --with-geos-config was given. if test ${with_geos_config+y} then : withval=$with_geos_config; geos_config=$withval fi if test -n "$geos_config" ; then GEOS_CONFIG_SET="yes" GEOS_CONFIG="${geos_config}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: geos-config set to $GEOS_CONFIG" >&5 printf "%s\n" "$as_me: geos-config set to $GEOS_CONFIG" >&6;} fi if test "$GEOS_CONFIG_SET" = "no" ; then # Extract the first word of ""$GEOS_CONFIG"", so it can be a program name with args. set dummy "$GEOS_CONFIG"; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_GEOS_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $GEOS_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_GEOS_CONFIG="$GEOS_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_GEOS_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_GEOS_CONFIG" && ac_cv_path_GEOS_CONFIG=""no"" ;; esac fi GEOS_CONFIG=$ac_cv_path_GEOS_CONFIG if test -n "$GEOS_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $GEOS_CONFIG" >&5 printf "%s\n" "$GEOS_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "$GEOS_CONFIG" = "no" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "geos-config not found or not executable." "$LINENO" 5 fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking geos-config exists" >&5 printf %s "checking geos-config exists... " >&6; } if test -r "${GEOS_CONFIG}"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "geos-config not found - configure argument error." "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking geos-config executable" >&5 printf %s "checking geos-config executable... " >&6; } if test -x "${GEOS_CONFIG}"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "geos-config not executable." "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking geos-config usability" >&5 printf %s "checking geos-config usability... " >&6; } if test `${GEOS_CONFIG} --version` then GEOS_CLIBS=`${GEOS_CONFIG} --clibs` #GEOS_DEP_CLIBS=`geos-config --static-clibs` -- this gives -m instead of -lm, which breaks clang # fixed in 3.7.0 at https://github.com/libgeos/libgeos/pull/73#issuecomment-262208677 GEOS_DEP_CLIBS=`${GEOS_CONFIG} --static-clibs | sed 's/-m/-lm/g'` GEOS_CPPFLAGS=`${GEOS_CONFIG} --cflags` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "${GEOS_CONFIG} not usable" "$LINENO" 5 fi GEOS_VERSION=`${GEOS_CONFIG} --version` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: GEOS: ${GEOS_VERSION}" >&5 printf "%s\n" "$as_me: GEOS: ${GEOS_VERSION}" >&6;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking GEOS version >= 3.5.0" >&5 printf %s "checking GEOS version >= 3.5.0... " >&6; } # PostGIS 3.0.0 requires this GEOS_VER_DOT=`echo $GEOS_VERSION | tr -d "."` if test ${GEOS_VER_DOT} -lt 350 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "upgrade GEOS to 3.5.0 or later" "$LINENO" 5 else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi # honor PKG_xx overrides # for CPPFLAGS we will superfluously double R's flags # since we'll set PKG_CPPFLAGS with this, but that shouldn't hurt PKG_CPPFLAGS="${PKG_CPPFLAGS} ${GEOS_CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PKG_CPPFLAGS}" LIBS="${LIBS} ${PKG_LIBS} ${GEOS_CLIBS}" PKG_CPPFLAGS="${PKG_CPPFLAGS} -DPOSTGIS_GEOS_VERSION=35" #geosok=yes #AC_CHECK_HEADERS(geos_c.h,,geosok=no) #if test "${geosok}" = no; then # AC_MSG_ERROR([geos_c.h not found in given locations.]) #fi cat > geos_test.cpp <<_EOCONF #include #ifdef __cplusplus extern "C" { #endif static void __errorHandler(const char *fmt, ...) { return; } static void __warningHandler(const char *fmt, ...) { return; } int main(void) { GEOSContextHandle_t r = initGEOS_r((GEOSMessageHandler) __warningHandler, (GEOSMessageHandler) __errorHandler); finishGEOS_r(r); } #ifdef __cplusplus } #endif _EOCONF #echo "${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${LIBS}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking geos: linking with ${GEOS_CLIBS}" >&5 printf %s "checking geos: linking with ${GEOS_CLIBS}... " >&6; } ${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${GEOS_CLIBS} 2> errors.txt if test `echo $?` -ne 0 ; then geosok=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else GEOS_LIBS="${GEOS_CLIBS}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi if test "${geosok}" = no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking geos: linking with ${GEOS_DEP_CLIBS}" >&5 printf %s "checking geos: linking with ${GEOS_DEP_CLIBS}... " >&6; } ${CXX} ${CPPFLAGS} -o geos_test geos_test.cpp ${GEOS_DEP_CLIBS} 2> errors.txt if test `echo $?` -ne 0 ; then geosok=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } cat errors.txt { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Install failure: compilation and/or linkage problems." >&5 printf "%s\n" "$as_me: Install failure: compilation and/or linkage problems." >&6;} as_fn_error $? "GEOS_init_r not found in libgeos_c." "$LINENO" 5 else GEOS_LIBS="${GEOS_DEP_CLIBS}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi fi rm -f geos_test errors.txt geos_test.cpp # # liblwgeom # OBJECTS="${OBJECTS} \$(OBJECTS_RCPP)" #AC_CHECK_LIB(proj, pj_init_plus, # AC_SUBST([PKG_LIBS], ["${PKG_LIBS} -lproj"]), # AC_MSG_ERROR([libproj not found a standard locations.])) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GEOS_init_r in -lgeos_c" >&5 printf %s "checking for GEOS_init_r in -lgeos_c... " >&6; } if test ${ac_cv_lib_geos_c_GEOS_init_r+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgeos_c $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char GEOS_init_r (); int main (void) { return GEOS_init_r (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_geos_c_GEOS_init_r=yes else $as_nop ac_cv_lib_geos_c_GEOS_init_r=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_geos_c_GEOS_init_r" >&5 printf "%s\n" "$ac_cv_lib_geos_c_GEOS_init_r" >&6; } if test "x$ac_cv_lib_geos_c_GEOS_init_r" = xyes then : PKG_LIBS="${LIBS} -lgeos_c" else $as_nop PKG_LIBS="${LIBS} ${PROJ_LIBS} ${GEOS_LIBS}" fi OBJECTS="${OBJECTS} \$(OBJECTS_LIBLWGEOM)" # Must keep the leading ${CPPFLAGS} or the previous CPPFLAGS don't get saved PKG_CPPFLAGS="${CPPFLAGS} ${PKG_CPPFLAGS} -I./liblwgeom -DHAVE_LIBGEOM_INTERNAL_H" # # concluding substitution # { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Package CPP flags: ${PKG_CPPFLAGS}" >&5 printf "%s\n" "$as_me: Package CPP flags: ${PKG_CPPFLAGS}" >&6;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Package LIBS: ${LIBS}" >&5 printf "%s\n" "$as_me: Package LIBS: ${LIBS}" >&6;} ac_config_files="$ac_config_files src/Makevars src/postgis_config.h" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to the package provider." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; "src/postgis_config.h") CONFIG_FILES="$CONFIG_FILES src/postgis_config.h" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi