diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..7d87b9d0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: Maven Build +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Upload geoscript-js.zip + uses: actions/upload-artifact@v2 + with: + name: geoscript-js.zip + path: target/geoscript-js-*.zip + - name: Upload geoscript-js-app.jar + uses: actions/upload-artifact@v2 + with: + name: geoscript-js-app + path: target/geoscript-js-*-app.jar \ No newline at end of file diff --git a/.gitignore b/.gitignore index 551f21a4..513dae5f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .project .settings/ /target/ -/data/ \ No newline at end of file +/data/ +.idea +*.iml \ No newline at end of file diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..b901097f --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..2cc7d4a5 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..642d572c --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/.travis.yml b/.travis.yml index f366fe67..692f4aa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ +dist: trusty + language: java jdk: - - openjdk6 - + - oraclejdk8 diff --git a/README.md b/README.md index 8ada4190..e3f44282 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![Current Status](https://secure.travis-ci.org/geoscript/geoscript-js.png?branch=master)](https://travis-ci.org/geoscript/geoscript-js) + +[![Build Status](https://github.com/geoscript/geoscript-js/workflows/Maven%20Build/badge.svg)](https://github.com/geoscript/geoscript-js/actions) + # geoscript.js ## GeoScript in JavaScript @@ -8,13 +12,13 @@ Released under the MIT license. Please see the license.txt for full detail. ### Download and Installation -The latest release of GeoScript JS can be found on the [downloads page](http://geoscript.org/js/download.html). To install, extract the zip archive somewhere onto your filesystem. In the `bin` folder you'll find a `geoscript` executable. Adding this `bin` folder to your path makes for easy launching of GeoScript from anywhere. +The latest release of GeoScript JS can be found on the [downloads page](http://geoscript.net/js/download.html). To install, extract the zip archive somewhere onto your filesystem. In the `bin` folder you'll find a `geoscript` executable. Adding this `bin` folder to your path makes for easy launching of GeoScript from anywhere. ### Running GeoScript JS Change into the directory where you extracted the GeoScript JS download. From there, you can launch the GeoScript JS shell. - ./bin/geoscript + ./bin/geoscript-js Once running the shell, you can pull in GeoScript modules with `require`. @@ -29,11 +33,11 @@ When you're done in the shell, exit with `quit()`. To run a script that uses the GeoScript JS modules, include the path to your script. - ./bin/geoscript yourscript.js + ./bin/geoscript-js yourscript.js ### Learning GeoScript JS -See the [GeoScript JS website](http://geoscript.org/js/) for details on getting started using GeoScript JS. +See the [GeoScript JS website](http://geoscript.net/js/) for details on getting started using GeoScript JS. ### Getting set up for development @@ -55,4 +59,3 @@ Deploy both the stand-alone archive and a jar with just the GeoScript JS modules mvn deploy -[![Current Status](https://secure.travis-ci.org/tschaub/geoscript-js.png?branch=master)](https://travis-ci.org/tschaub/geoscript-js) diff --git a/bin/geoscript b/bin/geoscript-js similarity index 100% rename from bin/geoscript rename to bin/geoscript-js diff --git a/bin/geoscript-dev b/bin/geoscript-js-dev similarity index 82% rename from bin/geoscript-dev rename to bin/geoscript-js-dev index ea3059b0..5a2c40a1 100755 --- a/bin/geoscript-dev +++ b/bin/geoscript-js-dev @@ -19,4 +19,4 @@ else GEOSCRIPT_HOME=$(dirname -- "$(dirname -- "$SELF_PATH")") fi -$GEOSCRIPT_HOME/bin/geoscript -j $GEOSCRIPT_HOME/target/jars -m $GEOSCRIPT_HOME/src/main/resources/org/geoscript/js/lib "$@" +$GEOSCRIPT_HOME/bin/geoscript-js -j $GEOSCRIPT_HOME/target/jars -m $GEOSCRIPT_HOME/src/main/resources/org/geoscript/js/lib "$@" diff --git a/bin/geoscript.cmd b/bin/geoscript-js.cmd similarity index 100% rename from bin/geoscript.cmd rename to bin/geoscript-js.cmd diff --git a/doc/api/geom.rst b/doc/api/geom.rst index be2ef21b..f0ffad7e 100644 --- a/doc/api/geom.rst +++ b/doc/api/geom.rst @@ -24,6 +24,8 @@ Constructors geom/multilinestring geom/multipolygon geom/bounds + geom/circularstring + geom/compoundcurve Module Data diff --git a/doc/api/geom/circularstring.rst b/doc/api/geom/circularstring.rst new file mode 100644 index 00000000..8419d08e --- /dev/null +++ b/doc/api/geom/circularstring.rst @@ -0,0 +1,58 @@ +:class:`geom.CircularString` +======================== + +.. class:: geom.CircularString(coords) + + :arg Array coords: Coordinates array. + + Create a new circularstring. + + +Example Use +----------- + +Sample code to new circularstring: + +.. code-block:: javascript + + >> var CircularString = require("geoscript/geom").CircularString; + >> var cs = new CircularString([[6.12, 10.0], [7.07, 7.07], [10.0, 0.0]]); + >> cs.controlPoints.length + 3 + >> cs.linear.getGeometryType() + LineString + >> cs.curvedWkt + CIRCULARSTRING (6.12 10.0, 7.07 7.07, 10.0 0.0) + + + +Properties +---------- + +In addition to the properties common to :class:`geom.LineString` subclasses, +circularstring geometries have the properties documented below. + + +.. attribute:: CircularString.curvedWkt + + :class:`String` + The curved WKT as a string. + +.. attribute:: CircularString.controlPoints + + ``Array`` + An array of the original control Points. + + +.. attribute:: CircularString.linear + + :class:`geom.LineString` + A linearized LineString. + + + +Methods +------- + +CircularString geometries have the methods common to :class:`geom.LineString` +subclasses. diff --git a/doc/api/geom/compoundcurve.rst b/doc/api/geom/compoundcurve.rst new file mode 100644 index 00000000..fe1628f4 --- /dev/null +++ b/doc/api/geom/compoundcurve.rst @@ -0,0 +1,56 @@ +:class:`geom.CompoundCurve` +=========================== + +.. class:: geom.CompoundCurve(lineStrings) + + :arg Array lineStrings: An array of LineStrings or CircularStrings. + + Create a new CompoundCurve. + + +Example Use +----------- + +Sample code to new compoundcurve: + +.. code-block:: javascript + + >> var GEOM = require("geoscript/geom"); + >> var cs = new GEOM.CircularString([[10.0, 10.0], [0.0, 20.0], [-10.0, 10.0]]); + >> var line = new GEOM.LineString([[-10.0, 10.0], [-10.0, 0.0], [10.0, 0.0], [5.0, 5.0]]) + >> var cc = new GEOM.CompoundCurve([cs, line]); + >> cc.components.length + 2 + >> cc.linear.getGeometryType() + LineString + >> cc.curvedWkt + COMPOUNDCURVE (CIRCULARSTRING (10.0 10.0, 0.0 20.0, -10.0 10.0), (-10.0 10.0, -10.0 0.0, 10.0 0.0, 5.0 5.0)) + + +Properties +---------- + +In addition to the properties common to all :class:`geom.LineString` subclasses, +compoundcurve geometries have the properties documented below. + + +.. attribute:: CompoundCurve.components + + :class:`geom.LineString` + The original LineString or CircularStrings. + +.. attribute:: CircularString.curvedWkt + + :class:`String` + The curved WKT as a string. + +.. attribute:: CircularString.linear + + :class:`geom.LineString` + A linearized LineString. + +Methods +------- + +CompoundCurve geometries have the methods common to all :class:`geom.LineString` +subclasses. diff --git a/doc/api/geom/geometry.rst b/doc/api/geom/geometry.rst index f0c94bdb..8e7669f5 100644 --- a/doc/api/geom/geometry.rst +++ b/doc/api/geom/geometry.rst @@ -99,6 +99,14 @@ Common Geometry Methods Construct a geometry that buffers this geometry by the given width. +.. function:: Geometry.variableBuffer + + :arg distances: ``Array`` An array of distances. + + :returns: :class:`geom.Geometry` + + Construct a geometry that buffers this geometry with an array of distances. + .. function:: Geometry.clone :returns: :class:`geom.Geometry` @@ -141,6 +149,16 @@ Common Geometry Methods Tests if this geometry crosses the other geometry. +.. function:: Geometry.densify + + :arg tolerance: ``Number`` The distance tolerance for the densification. + All line segments in the densified geometry will be no longer than the distance tolereance. + The tolerance value must be non-negative. + :returns: :class:`geom.Geometry` + + Densifies a geometry object adding vertices along the line segments of the + geometry. + .. function:: Geometry.difference :arg other: :class:`geom.Geometry` @@ -248,6 +266,13 @@ Common Geometry Methods :attr:`projection` of this geometry must be set before calling this method. Returns a new geometry. +.. function:: Geometry.getMaximumInscribedCircle + + :arg config: :`Object` tolerance property defaults to 1.0 + :returns: :class:`geom.Geometry` + + Get the maximum inscribed circle for this Geometry. + .. function:: Geometry.within :arg other: :class:`geom.Geometry` @@ -256,9 +281,10 @@ Common Geometry Methods Tests if this geometry is within the other geometry. This is the inverse of :func:`contains`. +.. function:: Geometry.getLargestEmptyCircle + :arg config: :`Object` tolerance property defaults to 1.0 + :returns: :class:`geom.Geometry` - - - + Get the largest empty circle in this Geometry. diff --git a/doc/api/geom/linestring.rst b/doc/api/geom/linestring.rst index d8ba5567..8ca523ba 100644 --- a/doc/api/geom/linestring.rst +++ b/doc/api/geom/linestring.rst @@ -53,3 +53,33 @@ Methods Linestring geometries have the methods common to all :class:`geom.Geometry` subclasses. + +.. function:: LineString.interpolatePoint + + :arg position: ``Number`` The position between 0 and 1. + :returns: :class:`geom.Point` + + Returns a Point placed on the LineString at the given percentage along + the LineString. + +.. function:: LineString.locatePoint + + :arg point: :class:`geom.Point` The Point + :returns: ``Number`` The position (0-1) or percentage of the Point along the LineString. + + Returns a position or percentage between 0 and 1 of the Point along the LineString. + +.. function:: LineString.placePoint + + :arg point: :class:`geom.Point` The Point. + :returns: :class:`geom.Point` The Point on the LineString. + + Places or snaps the Point to the LineString. + +.. function:: LineString.subLine + + :arg start: ``Number`` The start position between 0 and 1. + :arg end: ``Number`` The end position between 0 and 1. + :returns: :class:`geom.LineString` The sub LineString + + Returns a position or percentage between 0 and 1 of the Point along the LineString. \ No newline at end of file diff --git a/doc/api/index.rst b/doc/api/index.rst index 920597a9..89307846 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -16,8 +16,10 @@ Modules feature filter proj + index/* layer workspace + raster style map process diff --git a/doc/api/index/quadtree.rst b/doc/api/index/quadtree.rst new file mode 100644 index 00000000..e8e5697a --- /dev/null +++ b/doc/api/index/quadtree.rst @@ -0,0 +1,57 @@ +:class:`index.Quadtree` +========================== + +.. class:: index.Quadtree() + + Create a Quadtree Spatial Index. + + +Properties +---------- + +.. attribute:: size + + ``Int`` + The number of items in the spatial index. + + +Methods +------- + +.. function:: Quadtree.query + + :arg bounds: :class:`geom.Bounds` The Bounds. + :returns: :class:`Array` + + Query the spatial index by Bounds. + +.. function:: Quadtree.queryAll + + :returns: :class:`Array` + + Get all item in the spatial index. + +.. function:: Quadtree.insert + + :arg bounds: :class:`geom.Bounds` The Bounds. + :arg item: :class:`Object` The value. + :returns: :class:`boolean` Whether an item was removed or not + + Remove an item from the spatial index + +.. function:: Quadtree.remove + + :arg bounds: :class:`geom.Bounds` The Bounds. + :arg item: :class:`Object` The value. + :returns: :class:`boolean` Whether an item was removed or not + + Remove an item from the spatial index + + Get all item in the spatial index. + + + + + + + diff --git a/doc/api/index/strtree.rst b/doc/api/index/strtree.rst new file mode 100644 index 00000000..d009ef79 --- /dev/null +++ b/doc/api/index/strtree.rst @@ -0,0 +1,42 @@ +:class:`index.STRtree` +========================== + +.. class:: index.STRtree() + + Create a STRtree Spatial Index. + + +Properties +---------- + +.. attribute:: size + + ``Int`` + The number of items in the spatial index. + + +Methods +------- + +.. function:: STRtree.query + + :arg bounds: :class:`geom.Bounds` The Bounds. + :returns: :class:`Array` + + Query the spatial index by Bounds. + +.. function:: STRtree.insert + + :arg bounds: :class:`geom.Bounds` The Bounds. + :arg item: :class:`Object` The value. + :returns: :class:`boolean` Whether an item was removed or not + + Remove an item from the spatial index + + + + + + + + diff --git a/doc/api/process.rst b/doc/api/process.rst index 0304a5b3..c205a5c1 100644 --- a/doc/api/process.rst +++ b/doc/api/process.rst @@ -192,15 +192,15 @@ Static Methods >> var Process = require("geoscript/process").Process >> var Point = require("geoscript/geom").Point; - js> var buffer = Process.get("geo:buffer"); - js> Object.keys(buffer.inputs) + >> var buffer = Process.get("geo:buffer"); + >> Object.keys(buffer.inputs); geom,distance,quadrantSegments,capStyle - >> Object.keys(buffer.outputs) + >> Object.keys(buffer.outputs); result >> var point = Point([-110, 45]); - >> var outputs = buffer.run({geom: point, distance: 10}) - >> outputs.result + >> var outputs = buffer.run({geom: point, distance: 10}); + >> outputs.result; diff --git a/doc/api/raster.rst b/doc/api/raster.rst new file mode 100644 index 00000000..c7653c53 --- /dev/null +++ b/doc/api/raster.rst @@ -0,0 +1,232 @@ +The raster module +~~~~~~~~~~~~~~~~ + +The :doc:`raster ` module can read and write Rasters. + +.. code-block:: javascript + + >> var Format = require("geoscript/raster").Format; + +:class:`raster.Format` +==================== + +.. class:: raster.Format(config) + + Create a new Format. The config must contain a source representing the file or URL. + +Properties +---------- + +.. attribute:: Format.name + + ``String`` + The type of Format (GeoTIFF, WorldImage). + +.. attribute:: Format.names + + ``Array`` + Array of Raster names. Most Formats will only contain one Raster. + +Methods +------- + +.. function:: Format.read(config) + + :arg config: ``Object`` An object literal with parameters + + name: The name of the Raster (optional). Required if there are more than one Raster in the Format. + + proj: The Projection of the Raster (optional). + + bounds: The Bounds to read a subset of the entire Raster. Optional, but if included size must also be included. + + size: An array of width and height of the Raster. Optional, buf if included bound must also be included. + +.. function:: Format.write(raster, config) + + :arg raster: :class:`raster.Raster` The Raster to write to this Format. + + :arg config: ``Object`` An object literal of write parameters. + +:class:`raster.Raster` +==================== + +.. class:: raster.Raster + + A Raster is a spatial data set represented by a grid of cells organized in one or more bands. + + Usually, Rasters are read from a Format, but you can create a new Raster from an array or array of numeric values + and a geom.Bounds. + +Properties +---------- + +.. attribute:: Raster.name + + ``String`` + Get the name of the Raster. + +.. attribute:: Raster.proj + + :class:`proj.Projection` + Get the Projection. + + +.. attribute:: Raster.bounds + + :class:`geom.Bounds` + Get the Bounds. + +.. attribute:: Raster.size + + `Array` + Get the size of the Raster as an Array of two numbers: width and height + +.. attribute:: Raster.cols + + `Number` + Get the number of columns or the width or the Raster + +.. attribute:: Raster.rows + + `Number` + Get the number of row or the height or the Raster + +.. attribute:: Raster.bands + + `Array` of :class:`raster.Bands` + Get an array of Bands + +.. attribute:: Raster.extrema + + `Object` with min and max arrays with min and max values for each band + Get the minimum and maximum values for each band. + +.. attribute:: Raster.blockSize + + `Array` with width and height of a pixel + Get the block size + +.. attribute:: Raster.pixelSize + + `Array` with width and height of a pixel + Get the pixel size + +Methods +------- + +.. function:: Raster.getPixel(point) + + :arg point: :class:`geom.Point` The geographic Point + + Get a pixel ``Object`` with x and y properies. + +.. function:: Raster.getPoint(x,y) + + :arg x: ``Number`` The pixel's x position + + :arg y: ``Number`` The pixel's y position + + Get a :class:`geom.Point` for the pixel. + +.. function:: Raster.getValue(pointOrPixel) + + :arg pointOrPixel: ``Object`` The pixel or :class:`geom.Point` + + :arg type: ``String`` The type of value to return (double, int, float, byte, boolean) + + Get a value for each band from the Raster. + +.. function:: Raster.getMinimumValue(band) + + :arg namd: `Number` The band + + Get the minimum value for the given band + +.. function:: Raster.getMaximumValue(band) + + :arg namd: `Number` The band + + Get the maximum value for the given band + +.. function:: Raster.crop(bounds) + + :arg bounds: :class:`geom.Bound` The Bounds of the new Raster + + Crop the current Raster to only include data in the given Bounds. + +.. function:: Raster.crop(geometry) + + :arg geometry: :class:`geom.Geometry` The Geometry to use when cropping the Raster + + Crop the current Raster to only include data in the given Geometry. + +.. function:: Raster.reproject(projection) + + :arg projection: :class:`proj.Projection` The target Projection + + Reproject a Raster from one Projection to another Projection. + +.. function:: Raster.reclassify(ranges, options) + + :arg ranges: `Array` An array of object literals with required min, max, and value properties. minIncluded and maxIncluded are optional. + + :arg options: `Object` An object literal with optional band and noData values. + + Reclassify the values of the Raster. + +:class:`raster.Band` +==================== + +.. class:: raster.Band + + An individual layer from a Raster. + +Properties +---------- + +.. attribute:: Band.min + + ``Number`` + Get the minimum value from this Band. + +.. attribute:: Band.max + + ``Number`` + Get the maximum value from this Band. + +.. attribute:: Band.noData + + ``Array`` + Get the array of no data values. + + + +.. attribute:: Band.scale + + ``Number`` + Get the scale. + +.. attribute:: Band.scale + + ``Number`` + Get the scale. + +.. attribute:: Band.type + + ``Number`` + Get the Raster type. + +.. attribute:: Band.description + + ``Number`` + Get the Raster description. + +Methods +------- + +.. function:: Band.isNoData(value) + + :arg value: ``Object`` The value to check + + Determine whether the value is a no data value. \ No newline at end of file diff --git a/doc/api/types.rst b/doc/api/types.rst index 98cb20b9..91e18e17 100644 --- a/doc/api/types.rst +++ b/doc/api/types.rst @@ -73,7 +73,7 @@ mapping between JavaScript types and the corresponding Java types. - :class:`geom.Bounds` - org.geotools.geometry.jts.ReferencedEnvelope * - ``"FeatureCollection"`` - - :class:`feature.Collection` + - :class:`feature.FeatureCollection` - org.geotools.feature.FeatureCollection * - ``"Filter"`` - :class:`filter.Filter` diff --git a/doc/api/workspace/flatgeobuf.rst b/doc/api/workspace/flatgeobuf.rst new file mode 100644 index 00000000..94dbe4ca --- /dev/null +++ b/doc/api/workspace/flatgeobuf.rst @@ -0,0 +1,71 @@ +:class:`workspace.Flatgeobuf` +===================== + +.. class:: workspace.Flatgeobuf(config) + + :arg config: ``Object`` Configuration object. + + Create a workspace from an Flatgeobuf directory. + + +Config Properties +----------------- + +.. describe:: file + + ``String`` + Directory path to the Flatgeobuf files (required). + +Properties +---------- + +.. attribute:: Flatgeobuf.layers + + ``Array`` + The available layers in the workspace. + +.. attribute:: Flatgeobuf.names + + ``Array`` + The available layer names in the workspace. + + +Methods +------- + + +.. function:: Flatgeobuf.add + + :arg layer: :class:`layer.Layer` The layer to be added. + :arg options: ``Object`` Options for adding the layer. + + Options: + * `name`: ``String`` Name for the new layer. + * `filter`: :class:`filter.Filter` Filter to apply to features before adding. + * `projection`: :class:`proj.Projection` Destination projection for the layer. + + :returns: :class:`layer.Layer` + + Create a new layer in this workspace with the features from an existing + layer. If a layer with the same name already exists in this workspace, + you must provide a new name for the layer. + +.. function:: Flatgeobuf.close + + Close the workspace. This discards any existing connection to the + underlying data store and discards the reference to the store. + +.. function:: Flatgeobuf.get + + :arg name: ``String`` Layer name. + :returns: :class:`layer.Layer` + + Get a layer by name. Returns ``undefined`` if name doesn't correspond + to a layer source in the workspace. + + + + + + + diff --git a/doc/api/workspace/geobuf.rst b/doc/api/workspace/geobuf.rst new file mode 100644 index 00000000..a61214de --- /dev/null +++ b/doc/api/workspace/geobuf.rst @@ -0,0 +1,71 @@ +:class:`workspace.Geobuf` +===================== + +.. class:: workspace.Geobuf(config) + + :arg config: ``Object`` Configuration object. + + Create a workspace from an Geobuf directory. + + +Config Properties +----------------- + +.. describe:: file + + ``String`` + Directory path to the Geobuf files (required). + +Properties +---------- + +.. attribute:: Geobuf.layers + + ``Array`` + The available layers in the workspace. + +.. attribute:: Geobuf.names + + ``Array`` + The available layer names in the workspace. + + +Methods +------- + + +.. function:: Geobuf.add + + :arg layer: :class:`layer.Layer` The layer to be added. + :arg options: ``Object`` Options for adding the layer. + + Options: + * `name`: ``String`` Name for the new layer. + * `filter`: :class:`filter.Filter` Filter to apply to features before adding. + * `projection`: :class:`proj.Projection` Destination projection for the layer. + + :returns: :class:`layer.Layer` + + Create a new layer in this workspace with the features from an existing + layer. If a layer with the same name already exists in this workspace, + you must provide a new name for the layer. + +.. function:: Geobuf.close + + Close the workspace. This discards any existing connection to the + underlying data store and discards the reference to the store. + +.. function:: Geobuf.get + + :arg name: ``String`` Layer name. + :returns: :class:`layer.Layer` + + Get a layer by name. Returns ``undefined`` if name doesn't correspond + to a layer source in the workspace. + + + + + + + diff --git a/doc/api/workspace/geopackage.rst b/doc/api/workspace/geopackage.rst new file mode 100644 index 00000000..31a65458 --- /dev/null +++ b/doc/api/workspace/geopackage.rst @@ -0,0 +1,71 @@ +:class:`workspace.GeoPackage` +===================== + +.. class:: workspace.GeoPackage(config) + + :arg config: ``Object`` Configuration object. + + Create a workspace from an GeoPackage database. + + +Config Properties +----------------- + +.. describe:: database + + ``String`` + Path to the database (required). + +Properties +---------- + +.. attribute:: GeoPackage.layers + + ``Array`` + The available layers in the workspace. + +.. attribute:: GeoPackage.names + + ``Array`` + The available layer names in the workspace. + + +Methods +------- + + +.. function:: GeoPackage.add + + :arg layer: :class:`layer.Layer` The layer to be added. + :arg options: ``Object`` Options for adding the layer. + + Options: + * `name`: ``String`` Name for the new layer. + * `filter`: :class:`filter.Filter` Filter to apply to features before adding. + * `projection`: :class:`proj.Projection` Destination projection for the layer. + + :returns: :class:`layer.Layer` + + Create a new layer in this workspace with the features from an existing + layer. If a layer with the same name already exists in this workspace, + you must provide a new name for the layer. + +.. function:: GeoPackage.close + + Close the workspace. This discards any existing connection to the + underlying data store and discards the reference to the store. + +.. function:: GeoPackage.get + + :arg name: ``String`` Layer name. + :returns: :class:`layer.Layer` + + Get a layer by name. Returns ``undefined`` if name doesn't correspond + to a layer source in the workspace. + + + + + + + diff --git a/doc/download.rst b/doc/download.rst index d13cf4c1..bb1d85cc 100644 --- a/doc/download.rst +++ b/doc/download.rst @@ -3,10 +3,17 @@ Downloads ========= -`GeoScript JS 0.9.0 `__ +`GeoScript JS 1.7.0 `__ -.. warning:: +`GeoScript JS 1.6.0 `__ - Pre 1.0 releases are subject to API change. +`GeoScript JS 1.5.0 `__ +`GeoScript JS 1.4.0 `__ + +`GeoScript JS 1.3.0 `__ + +`GeoScript JS 1.2.0 `__ + +`GeoScript JS 1.1.0 `__ diff --git a/doc/index.rst b/doc/index.rst index 94eda6fd..0db00345 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,8 +1,7 @@ GeoScript JS ============ -GeoScript JS is a JavaScript implementation of GeoScript packaged for the -RingoJS_ platform (or any other CommonJS_ module loader based on Rhino). +GeoScript JS is a JavaScript implementation of GeoScript packaged as a set of CommonJS_ modules for Rhino. :ref:`Quickstart ` Get up and running with GeoScript @@ -25,5 +24,4 @@ RingoJS_ platform (or any other CommonJS_ module loader based on Rhino). download -.. _RingoJS: http://ringojs.org/ .. _CommonJS: http://commonjs.org/ \ No newline at end of file diff --git a/doc/quickstart.rst b/doc/quickstart.rst index c971aad8..bc34c0da 100644 --- a/doc/quickstart.rst +++ b/doc/quickstart.rst @@ -6,7 +6,7 @@ Quick Start Getting GeoScript JS -------------------- -To install GeoScript JS, `download the latest `__ release and extract the zip archive. +To install GeoScript JS, :ref:`download the latest ` release and extract the zip archive. Running GeoScript JS -------------------- @@ -15,7 +15,7 @@ After extracting the release archive, you can open the GeoScript shell and impor .. code-block:: javascript - ~/geoscript-js$ ./bin/geoscript + ~/geoscript-js$ ./bin/geoscript-js >> var geom = require("geoscript/geom"); >> var p1 = new geom.Point([0, 0]); @@ -27,3 +27,6 @@ After extracting the release archive, you can open the GeoScript shell and impor true >> quit() +You can also use the uber jar that contains all dependencies and is runnable:: + + java -jar geoscript-js-1.7.0-app.jar diff --git a/examples/geometry/delaunayTriangle.js b/examples/geometry/delaunayTriangle.js new file mode 100644 index 00000000..e5afedda --- /dev/null +++ b/examples/geometry/delaunayTriangle.js @@ -0,0 +1,7 @@ +var geom = require('geoscript/geom'); +var viewer = require('geoscript/viewer'); + +var polygon = geom.Point([1,1]).buffer(50); +var points = polygon.randomPoints(100); +var triangles = points.createDelaunayTriangles(true); +viewer.draw(triangles); \ No newline at end of file diff --git a/examples/geometry/voronoiDigram.js b/examples/geometry/voronoiDigram.js new file mode 100644 index 00000000..57084d3b --- /dev/null +++ b/examples/geometry/voronoiDigram.js @@ -0,0 +1,7 @@ +var geom = require('geoscript/geom'); +var viewer = require('geoscript/viewer'); + +var polygon = geom.Point([1,1]).buffer(50); +var points = polygon.randomPoints(20); +var diagram = points.createVoronoiDiagram(); +viewer.draw(diagram); \ No newline at end of file diff --git a/examples/raster/raster.pgw b/examples/raster/raster.pgw new file mode 100644 index 00000000..945ce57a --- /dev/null +++ b/examples/raster/raster.pgw @@ -0,0 +1,6 @@ +0.4 +0.0 +0.0 +-0.4 +-179.8 +89.8 diff --git a/examples/raster/raster.png b/examples/raster/raster.png new file mode 100644 index 00000000..c4a5d07f Binary files /dev/null and b/examples/raster/raster.png differ diff --git a/examples/raster/raster.prj b/examples/raster/raster.prj new file mode 100644 index 00000000..88167c95 --- /dev/null +++ b/examples/raster/raster.prj @@ -0,0 +1,9 @@ +GEOGCS["WGS 84", + DATUM["World Geodetic System 1984", + SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], + AUTHORITY["EPSG","6326"]], + PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], + UNIT["degree", 0.017453292519943295], + AXIS["Geodetic longitude", EAST], + AXIS["Geodetic latitude", NORTH], + AUTHORITY["EPSG","4326"]] \ No newline at end of file diff --git a/examples/raster/raster.tif b/examples/raster/raster.tif new file mode 100755 index 00000000..d2ae72c6 Binary files /dev/null and b/examples/raster/raster.tif differ diff --git a/examples/raster/readRaster.js b/examples/raster/readRaster.js new file mode 100644 index 00000000..17a739e7 --- /dev/null +++ b/examples/raster/readRaster.js @@ -0,0 +1,13 @@ +var raster = require('geoscript/raster'); +var proj = require('geoscript/proj'); + +var format = new raster.Format({source: 'raster.tif'}); +print("Format names: " + format.names); +var tif = format.read({}); +print("Raster: " + tif); +print("Name: " + tif.name); +print("Projection: " + tif.proj); +print("Size: " + tif.size); + +var pngFormat = new raster.Format({source: 'raster.png'}); +pngFormat.write(tif, {}); \ No newline at end of file diff --git a/lib/ringo-core.jar b/lib/ringo-core.jar new file mode 100644 index 00000000..ead1fa8b Binary files /dev/null and b/lib/ringo-core.jar differ diff --git a/lib/ringo-modules.jar b/lib/ringo-modules.jar new file mode 100644 index 00000000..82ea2c5a Binary files /dev/null and b/lib/ringo-modules.jar differ diff --git a/license.txt b/license.txt index c326b1c0..7024d621 100644 --- a/license.txt +++ b/license.txt @@ -1,3 +1,5 @@ +## The MIT License (MIT) + Copyright (c) 2009-2013 Tim Schaub Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..41c0f0c2 --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..86115719 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index fbe321b3..25c31916 100644 --- a/pom.xml +++ b/pom.xml @@ -4,33 +4,42 @@ org.geoscript geoscript-js jar - 0.9.0 + 1.8.0-SNAPSHOT - 9-SNAPSHOT + 29-SNAPSHOT UTF-8 UTF-8 - opengeo - OpenGeo Maven Repository - http://repo.opengeo.org/ + osgeo-releases + OSGeo Nexus Release Repository + https://repo.osgeo.org/repository/release/ + + false + + + true + + + + osgeo-snapshots + OSGeo Nexus Snapshot Repository + https://repo.osgeo.org/repository/snapshot/ true + + false + - - com.vividsolutions - jts - 1.12 - junit junit - 4.11 + 4.13.1 test @@ -39,13 +48,21 @@ org.ringojs ringojs - [0.8.1] - test + 1.2.1 + ${basedir}/lib/ringo-core.jar + system + + + org.ringojs + ringojs-modules + 1.2.1 + ${basedir}/lib/ringo-modules.jar + system org.mozilla rhino - 1.7R4 + 1.7.13 jline @@ -72,6 +89,11 @@ gt-process-feature ${gt.version} + + org.geotools + gt-process-raster + ${gt.version} + org.geotools gt-epsg-hsql @@ -109,11 +131,6 @@ gt-jdbc-mysql ${gt.version} - - org.geotools.jdbc - gt-jdbc-spatialite - ${gt.version} - org.geotools gt-property @@ -129,6 +146,31 @@ gt-swing ${gt.version} + + org.geotools + gt-geopkg + ${gt.version} + + + org.geotools + gt-geobuf + ${gt.version} + + + org.geotools + gt-flatgeobuf + ${gt.version} + + + org.geotools + gt-geotiff + ${gt.version} + + + org.geotools + gt-image + ${gt.version} + @@ -137,13 +179,18 @@ maven-compiler-plugin 2.3 - 1.5 - 1.5 + 1.8 + 1.8 true UTF-8 true + + org.apache.maven.plugins + maven-surefire-plugin + 2.10 + true org.apache.maven.plugins @@ -200,10 +247,13 @@ maven-assembly-plugin - 2.2-beta-2 + 3.3.0 - src/main/assembly/all.xml - geoscript-js-shell-${project.version} + + src/main/assembly/all.xml + + geoscript-js-${project.version} + false @@ -239,6 +289,53 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + true + app + + + META-INF/spring.handlers + + + META-INF/spring.schemas + + + org.geoscript.js.GeoScriptShell + + Java Advanced Imaging Image I/O Tools + 1.1 + Sun Microsystems, Inc. + com.sun.media.imageio + 1.1 + Sun Microsystems, Inc. + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + @@ -250,16 +347,16 @@ - opengeo - OpenGeo Maven Repository - dav:http://repo.opengeo.org + nexus + OSGeo Release Repository + https://repo.osgeo.org/repository/geotools-releases/ false - opengeo - OpenGeo Maven Repository - dav:http://repo.opengeo.org + nexus false + OSGeo Snapshot Repository + https://repo.osgeo.org/repository/geotools-snapshots/ diff --git a/src/main/assembly/all.xml b/src/main/assembly/all.xml index 3fbad418..1dda5d6d 100644 --- a/src/main/assembly/all.xml +++ b/src/main/assembly/all.xml @@ -1,6 +1,8 @@ + dist + dir zip @@ -14,7 +16,7 @@ bin 0755 - geoscript-dev + geoscript-js-dev diff --git a/src/main/java/org/geoscript/js/GeoObject.java b/src/main/java/org/geoscript/js/GeoObject.java index 2bf5ca5e..1e6d5cbd 100644 --- a/src/main/java/org/geoscript/js/GeoObject.java +++ b/src/main/java/org/geoscript/js/GeoObject.java @@ -45,14 +45,14 @@ protected enum Type { Number(Double.class), Double(Double.class), Boolean(Boolean.class), - Geometry(com.vividsolutions.jts.geom.Geometry.class), - Point(com.vividsolutions.jts.geom.Point.class), - LineString(com.vividsolutions.jts.geom.LineString.class), - Polygon(com.vividsolutions.jts.geom.Polygon.class), - GeometryCollection(com.vividsolutions.jts.geom.GeometryCollection.class), - MultiPoint(com.vividsolutions.jts.geom.MultiPoint.class), - MultiLineString(com.vividsolutions.jts.geom.MultiLineString.class), - MultiPolygon(com.vividsolutions.jts.geom.MultiPolygon.class), + Geometry(org.locationtech.jts.geom.Geometry.class), + Point(org.locationtech.jts.geom.Point.class), + LineString(org.locationtech.jts.geom.LineString.class), + Polygon(org.locationtech.jts.geom.Polygon.class), + GeometryCollection(org.locationtech.jts.geom.GeometryCollection.class), + MultiPoint(org.locationtech.jts.geom.MultiPoint.class), + MultiLineString(org.locationtech.jts.geom.MultiLineString.class), + MultiPolygon(org.locationtech.jts.geom.MultiPolygon.class), Bounds(ReferencedEnvelope.class), FeatureCollection(org.geotools.feature.FeatureCollection.class), Filter(org.opengis.filter.Filter.class), @@ -333,8 +333,8 @@ public static Object javaToJS(Object value, Scriptable scope) { Object[] args = { new Long(date.getTime()) }; Context cx = getCurrentContext(); value = cx.newObject(scope, "Date", args); - } else if (value instanceof com.vividsolutions.jts.geom.Geometry) { - value = GeometryWrapper.wrap(scope, (com.vividsolutions.jts.geom.Geometry) value); + } else if (value instanceof org.locationtech.jts.geom.Geometry) { + value = GeometryWrapper.wrap(scope, (org.locationtech.jts.geom.Geometry) value); } else if (value instanceof ReferencedEnvelope) { value = new Bounds(scope, (ReferencedEnvelope) value); } else if (value instanceof SimpleFeature) { diff --git a/src/main/java/org/geoscript/js/feature/FeatureCollection.java b/src/main/java/org/geoscript/js/feature/FeatureCollection.java index 753d32c7..66d3e1c7 100644 --- a/src/main/java/org/geoscript/js/feature/FeatureCollection.java +++ b/src/main/java/org/geoscript/js/feature/FeatureCollection.java @@ -6,11 +6,13 @@ import org.geoscript.js.GeoObject; import org.geoscript.js.geom.Bounds; +import org.geotools.data.DataUtilities; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.process.vector.SimpleProcessingCollection; import org.geotools.util.logging.Logging; +import org.locationtech.jts.geom.Envelope; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.JavaScriptException; @@ -354,7 +356,7 @@ public SimpleFeatureIterator features() { @Override public ReferencedEnvelope getBounds() { - return getFeatureBounds(); + return DataUtilities.bounds(features()); } @Override @@ -465,7 +467,7 @@ public ReferencedEnvelope getBounds() { throw ScriptRuntime.constructError("Error", "The bounds function must return a bounds. Got: " + Context.toString(retObj)); } } else { - refEnv = getFeatureBounds(); + refEnv = DataUtilities.bounds(features()); } return refEnv; } @@ -496,7 +498,7 @@ public int size() { } size = (int) Context.toNumber(retObj); } else { - size = getFeatureCount(); + size = DataUtilities.count(features()); } return size; } diff --git a/src/main/java/org/geoscript/js/filter/Expression.java b/src/main/java/org/geoscript/js/filter/Expression.java new file mode 100644 index 00000000..9ce42b29 --- /dev/null +++ b/src/main/java/org/geoscript/js/filter/Expression.java @@ -0,0 +1,167 @@ +package org.geoscript.js.filter; + +import org.geoscript.js.GeoObject; +import org.geotools.factory.CommonFactoryFinder; +import org.geotools.filter.text.cql2.CQL; +import org.geotools.filter.text.cql2.CQLException; +import org.geotools.filter.text.ecql.ECQL; +import org.mozilla.javascript.*; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSGetter; +import org.mozilla.javascript.annotations.JSSetter; +import org.mozilla.javascript.annotations.JSStaticFunction; +import org.opengis.filter.FilterFactory; +import org.opengis.filter.expression.Literal; + +public class Expression extends GeoObject implements Wrapper { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 4426338466323185386L; + + /** + * The GeoTools Expression + */ + private org.opengis.filter.expression.Expression expression; + + private static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null); + + /** + * The prototype constructor + */ + public Expression() { + } + + public Expression(org.opengis.filter.expression.Expression expression) { + this.expression = expression; + } + + public Expression(Object value) { + this(filterFactory.literal(value)); + } + + public Expression(Scriptable scope, org.opengis.filter.expression.Expression expression) { + this(expression); + setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Expression.class)); + } + + public Expression(Scriptable scope, Object value) { + this(scope, filterFactory.literal(value)); + } + + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) { + if (args.length == 0) { + return new Expression(); + } + Expression expression = null; + Object arg = args[0]; + Object value = null; + if (arg instanceof String || arg instanceof Number) { + value = arg; + } else if (arg instanceof NativeObject) { + NativeObject config = (NativeObject) arg; + value = config.get("text", config); + } else { + throw ScriptRuntime.constructError("Error", "Cannot create Expression from provided value: " + Context.toString(ctorObj)); + } + if (inNewExpr) { + expression = new Expression(toExpression(value.toString())); + } else { + expression = new Expression(ctorObj.getParentScope(), toExpression(value.toString())); + } + return expression; + } + + @JSGetter + public String getText() { + String text; + if (this.getLiteral()) { + Object value = ((Literal) this.expression).getValue(); + if (value instanceof String) { + text = "'" + value + "'"; + } else { + text = value.toString(); + } + } else { + text = this.expression.toString(); + } + return text; + } + + @JSSetter + public void setText(String text) { + this.expression = toExpression(text); + } + + private static org.opengis.filter.expression.Expression toExpression(String text) { + try { + return ECQL.toExpression(text); + } catch (CQLException ex1) { + try { + return CQL.toExpression(text); + } catch (CQLException ex2) { + throw ScriptRuntime.constructError("Error", + "Cannot parse the following text with " + + "CQL (" + ex2.getMessage() + ") or ECQL (" + ex1.getMessage() + ")!"); + } + } + } + + // TODO: Remove once Style API has been converted + @JSGetter("_expression") + public Object getExpression() { + return this.expression; + } + + @JSGetter + public boolean getLiteral() { + return this.expression instanceof Literal; + } + + @JSGetter + public Scriptable getConfig() { + Scriptable config = super.getConfig(); + config.put("type", config, "Expression"); + config.put("text", config, getText()); + return config; + } + + @Override + public String toFullString() { + return getText(); + } + + @Override + public String getClassName() { + return getClass().getName(); + } + + @Override + public org.opengis.filter.expression.Expression unwrap() { + return this.expression; + } + + @JSStaticFunction("from_") + public static Expression from(Scriptable scriptable) { + org.opengis.filter.expression.Expression expr = null; + if (scriptable instanceof Wrapper) { + Object obj = ((Wrapper) scriptable).unwrap(); + if (obj instanceof org.opengis.filter.expression.Expression) { + expr = (org.opengis.filter.expression.Expression) obj; + } + } + if (expr == null) { + throw ScriptRuntime.constructError("Error", "Cannot create expression from " + Context.toString(scriptable)); + } + return new Expression(getTopLevelScope(scriptable), expr); + } + + @JSStaticFunction + public static Expression literal(Scriptable valueObj) { + Object value = valueObj.getDefaultValue(null); + return new Expression(getTopLevelScope(valueObj), value); + } +} \ No newline at end of file diff --git a/src/main/java/org/geoscript/js/filter/Filter.java b/src/main/java/org/geoscript/js/filter/Filter.java new file mode 100644 index 00000000..26b87312 --- /dev/null +++ b/src/main/java/org/geoscript/js/filter/Filter.java @@ -0,0 +1,263 @@ +package org.geoscript.js.filter; + +import org.geoscript.js.GeoObject; +import org.geoscript.js.feature.Feature; +import org.geotools.factory.CommonFactoryFinder; +import org.geotools.util.factory.GeoTools; +import org.geotools.filter.text.cql2.CQL; +import org.geotools.filter.text.cql2.CQLException; +import org.geotools.filter.text.ecql.ECQL; +import org.geotools.xsd.Encoder; +import org.mozilla.javascript.*; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; +import org.mozilla.javascript.annotations.JSStaticFunction; +import org.opengis.filter.FilterFactory2; +import org.opengis.filter.identity.FeatureId; + +import javax.xml.namespace.QName; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Filter extends GeoObject implements Wrapper { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 4426338466323185386L; + + private org.opengis.filter.Filter filter; + + private static FilterFactory2 factory = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints()); + + /** + * The Prototype constructor + */ + public Filter() { + } + + public Filter(org.opengis.filter.Filter filter) { + this.filter = filter; + } + + public Filter(Scriptable scope, org.opengis.filter.Filter filter) { + this(filter); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Filter.class)); + } + + public Filter(String cql) { + this(fromCQL(cql)); + } + + public Filter(Scriptable scope, String cql) { + this(scope, fromCQL(cql)); + } + + /** + * Create a GeoTools Filter from a CQL String + */ + private static org.opengis.filter.Filter fromCQL(String cql) { + org.opengis.filter.Filter filter; + try { + filter = ECQL.toFilter(cql); + } catch (CQLException ex1) { + try { + filter = CQL.toFilter(cql); + } catch (CQLException ex2) { + throw ScriptRuntime.constructError("Error", "Can't parse Filter CQL Expression: " + ex2.getMessage() + + " or ECQL Expression: " + ex1.getMessage()); + } + } + return filter; + } + + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean isNewExpr) { + if (args.length != 1) { + return new Filter(); + } + Filter filter; + Object arg = args[0]; + String cql; + if (arg instanceof String) { + cql = (String) arg; + } else if (arg instanceof NativeObject) { + NativeObject config = (NativeObject) arg; + cql = config.get("cql", config).toString(); + } else if (arg instanceof Filter) { + cql = ((Filter) arg).getCql(); + } else { + throw ScriptRuntime.constructError("Error", "Cannot create filter from provided value: " + Context.toString(ctorObj)); + } + if (isNewExpr) { + filter = new Filter(cql); + } else { + filter = new Filter(ctorObj.getParentScope(), cql); + } + return filter; + } + + @JSFunction + public boolean evaluate(Feature feature) { + return filter.evaluate(feature.unwrap()); + } + + @JSGetter + public Filter getNot() { + return new Filter(factory.not(this.filter)); + } + + @JSFunction + public Filter and(Scriptable filters) { + if (filters instanceof NativeArray) { + NativeArray array = (NativeArray) filters; + array.add(this.filter); + return Filter.staticAnd(array); + } else { + org.opengis.filter.Filter filter = factory.and(this.filter, ((Filter) filters).unwrap()); + return new Filter(getTopLevelScope(filters), filter); + } + } + + @JSFunction + public Filter or(Scriptable filters) { + if (filters instanceof NativeArray) { + NativeArray array = (NativeArray) filters; + array.add(this.filter); + return Filter.staticOr(array); + } else { + org.opengis.filter.Filter filter = factory.or(this.filter, ((Filter) filters).unwrap()); + return new Filter(getTopLevelScope(filters), filter); + } + } + + @JSGetter + public String getCql() { + return ECQL.toCQL(this.filter); + } + + @JSGetter("_filter") + public org.opengis.filter.Filter getFilter() { + return this.filter; + } + + @JSGetter + @Override + public Scriptable getConfig() { + Scriptable obj = super.getConfig(); + obj.put("type", obj, "Filter"); + obj.put("text", obj, getCql()); + return obj; + } + + @JSFunction + public String toXML(String version, boolean pretty) throws IOException { + org.geotools.xsd.Encoder encoder; + QName qname; + if (version.equalsIgnoreCase("1.1")) { + qname = org.geotools.filter.v1_1.OGC.getInstance().Filter; + org.geotools.filter.v1_1.OGCConfiguration config = new org.geotools.filter.v1_1.OGCConfiguration(); + encoder = new Encoder(config); + } else { + qname = org.geotools.filter.v1_0.OGC.getInstance().Filter; + org.geotools.filter.v1_0.OGCConfiguration config = new org.geotools.filter.v1_0.OGCConfiguration(); + encoder = new Encoder(config); + } + encoder.setIndenting(pretty); + encoder.setOmitXMLDeclaration(true); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + encoder.encode(this.filter, qname, out); + return new String(out.toByteArray()); + } + + @Override + public String toFullString() { + return getCql(); + } + + @Override + public org.opengis.filter.Filter unwrap() { + return filter; + } + + @Override + public String getClassName() { + return getClass().getName(); + } + + private static List getFilters(NativeArray array) { + List filters = new ArrayList<>(); + for (int i = 0; i < array.size(); i++) { + Object item = array.get(i); + Filter filter; + if (item instanceof Filter) { + filter = (Filter) item; + } else if (item instanceof org.opengis.filter.Filter) { + filter = new Filter(getTopLevelScope(array), (org.opengis.filter.Filter) item); + } else { + filter = new Filter(getTopLevelScope(array), array.get(i).toString()); + } + filters.add(filter.unwrap()); + } + return filters; + } + + @JSStaticFunction("from_") + public static Filter from(Scriptable filterObject) { + org.opengis.filter.Filter filter = null; + if (filterObject instanceof Wrapper) { + Object obj = ((Wrapper) filterObject).unwrap(); + if (obj instanceof org.opengis.filter.Filter) { + filter = (org.opengis.filter.Filter) obj; + } + } + if (filter == null) { + throw ScriptRuntime.constructError("Error", "Cannot create filter from " + Context.toString(filterObject)); + } + return new Filter(getTopLevelScope(filterObject), filter); + } + + @JSStaticFunction("and") + public static Filter staticAnd(NativeArray array) { + List filters = getFilters(array); + return new Filter(getTopLevelScope(array), factory.and(filters)); + } + + @JSStaticFunction("or") + public static Filter staticOr(NativeArray array) { + List filters = getFilters(array); + return new Filter(getTopLevelScope(array), factory.or(filters)); + } + + @JSStaticFunction("not") + public static Filter staticNot(Scriptable obj) { + Filter filter; + String cql; + if (obj instanceof NativeObject) { + NativeObject config = (NativeObject) obj; + cql = config.get("cql", config).toString(); + } else if (obj.getDefaultValue(null) instanceof String) { + cql = obj.getDefaultValue(null).toString(); + } else { + throw ScriptRuntime.constructError("Error", "Cannot create filter from provided value: " + Context.toString(obj)); + } + filter = new Filter(cql); + return new Filter(getTopLevelScope(obj), factory.not(filter.unwrap())); + } + + @JSStaticFunction + public static Filter fids(NativeArray array) { + Set featureIds = new HashSet(); + for (int i = 0; i < array.size(); i++) { + featureIds.add(factory.featureId(array.get(i).toString())); + } + return new Filter(getTopLevelScope(array), factory.id(featureIds)); + } + +} diff --git a/src/main/java/org/geoscript/js/filter/Module.java b/src/main/java/org/geoscript/js/filter/Module.java new file mode 100644 index 00000000..e4b2024a --- /dev/null +++ b/src/main/java/org/geoscript/js/filter/Module.java @@ -0,0 +1,50 @@ +package org.geoscript.js.filter; + +import org.geoscript.js.GeoObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class Module { + + static HashMap prototypes; + + /** + * Define all filter constructors in the given module scope. If the + * provided scope is not a "top level" scope, constructors will be defined + * in the top level scope for the given scope. + * @param scope + * @throws IllegalAccessException + * @throws InstantiationException + * @throws java.lang.reflect.InvocationTargetException + */ + public static void init(Scriptable scope) throws IllegalAccessException, InstantiationException, InvocationTargetException { + + scope = ScriptableObject.getTopLevelScope(scope); + + @SuppressWarnings("unchecked") + List> classes = Arrays.asList(Filter.class, Expression.class); + + prototypes = new HashMap(); + for (Class cls : classes) { + String name = ScriptableObject.defineClass(scope, cls, false, true); + Scriptable prototype = ScriptableObject.getClassPrototype(scope, name); + prototypes.put(name, prototype); + } + + } + + protected static Scriptable getClassPrototype(Class cls) { + String name = cls.getName(); + if (prototypes == null || !prototypes.containsKey(name)) { + throw new RuntimeException( + "Attempt to access prototype before requiring module: " + name); + } + return prototypes.get(name); + } + +} diff --git a/src/main/java/org/geoscript/js/geom/Bounds.java b/src/main/java/org/geoscript/js/geom/Bounds.java index 0854e02e..136f6bf5 100644 --- a/src/main/java/org/geoscript/js/geom/Bounds.java +++ b/src/main/java/org/geoscript/js/geom/Bounds.java @@ -17,7 +17,7 @@ import org.mozilla.javascript.annotations.JSStaticFunction; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import com.vividsolutions.jts.geom.Envelope; +import org.locationtech.jts.geom.Envelope; public class Bounds extends GeoObject implements Wrapper { @@ -143,7 +143,7 @@ public Bounds(Scriptable scope, NativeArray array) { /** * Constructor from ReferencedEnvelope. * @param scope - * @param crs + * @param refEnv */ public Bounds(Scriptable scope, ReferencedEnvelope refEnv) { this.setParentScope(scope); @@ -291,9 +291,9 @@ public NativeArray toArray() { Scriptable scope = getParentScope(); return (NativeArray) cx.newArray(scope, new Object[] {getMinX(), getMinY(), getMaxX(), getMaxY()}); } - - @JSFunction - public Bounds clone() { + + @JSFunction("clone") + public Bounds cloner() { ReferencedEnvelope clone = new ReferencedEnvelope(refEnv); return new Bounds(getParentScope(), clone); } diff --git a/src/main/java/org/geoscript/js/geom/CircularString.java b/src/main/java/org/geoscript/js/geom/CircularString.java new file mode 100644 index 00000000..b356d42f --- /dev/null +++ b/src/main/java/org/geoscript/js/geom/CircularString.java @@ -0,0 +1,139 @@ +package org.geoscript.js.geom; + +import org.locationtech.jts.geom.Coordinate; +import org.geotools.geometry.jts.CurvedGeometryFactory; +import org.mozilla.javascript.*; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSGetter; + +import java.util.ArrayList; +import java.util.List; + +public class CircularString extends LineString implements Wrapper { + + /** serialVersionUID */ + private static final long serialVersionUID = -5048539260091857410L; + + /** + * Prototype constructor. + * @return + */ + public CircularString() { + } + + /** + * Constructor for config object. + * @param config + */ + public CircularString(NativeObject config) { + NativeArray array = (NativeArray) config.get("coordinates", config); + double tolerance = config.has("tolerance", config) ? (Double) config.get("tolerance", config) : Double.MAX_VALUE; + Coordinate[] coords = arrayToCoords(array); + setGeometry(createCircularString(coords, tolerance)); + } + + /** + * Constructor for config object (without new keyword). + * @param scope + * @param config + */ + public CircularString(Scriptable scope, NativeObject config) { + this(config); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(CircularString.class)); + } + + /** + * Constructor from JTS geometry. + * @param geometry + */ + public CircularString(Scriptable scope, org.geotools.geometry.jts.CircularString geometry) { + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(CircularString.class)); + setGeometry(geometry); + } + + /** + * JavaScript constructor. + * @param cx + * @param args + * @param ctorObj + * @param inNewExpr + * @return + */ + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) { + if (args.length != 1) { + throw ScriptRuntime.constructError("Error", "CircularString constructor takes a single argument"); + } + NativeObject config = prepConfig(cx, (Scriptable) args[0]); + CircularString line = null; + if (inNewExpr) { + line = new CircularString(config); + } else { + line = new CircularString(config.getParentScope(), config); + } + return line; + } + + /** + * Create a CircularString from an array of Coordinates and a tolerance used to linearize the curve. + * @param coords The Array of Coordinates + * @param tolerance The tolerance used to linearize the curve + * @return A CircularString + */ + private org.locationtech.jts.geom.Geometry createCircularString(Coordinate[] coords, double tolerance) { + CurvedGeometryFactory factory = new CurvedGeometryFactory(tolerance); + double[] values = new double[coords.length * 2]; + for(int i = 0; i < coords.length; i++) { + int c = i * 2; + values[c] = coords[i].x; + values[c + 1] = coords[i].y; + } + return new org.geotools.geometry.jts.CircularString(values, factory, tolerance); + } + + /** + * Get the curved WKT + * @return The curved WKT + */ + @JSGetter + public String getCurvedWkt() { + return ((org.geotools.geometry.jts.CircularString)getGeometry()).toCurvedText(); + } + + /** + * Get the original control Points (not the linearized Points) + * @return The original control Points + */ + @JSGetter + public NativeArray getControlPoints() { + Context cx = getCurrentContext(); + List points = new ArrayList<>(); + org.geotools.geometry.jts.CircularString cs = (org.geotools.geometry.jts.CircularString)getGeometry(); + double[] cp = cs.getControlPoints(); + for(int i=0; i lines = new ArrayList(); + for (int i=0; i lineStrings = new ArrayList(); + List lines = cs.getComponents(); + for (org.locationtech.jts.geom.LineString line : lines) { + lineStrings.add((LineString)GeometryWrapper.wrap(getParentScope(), line)); + } + return (NativeArray) cx.newArray(getParentScope(), lineStrings.toArray()); + } + + /** + * Get the linearized Geometry + * @return The linearized Geometry + */ + @JSGetter + public Geometry getLinear() { + org.geotools.geometry.jts.CompoundCurve cs = (org.geotools.geometry.jts.CompoundCurve)getGeometry(); + return (Geometry) GeometryWrapper.wrap(getParentScope(), cs.linearize()); + } + + /** + * Returns underlying JTS geometry. + */ + public org.geotools.geometry.jts.CompoundCurve unwrap() { + return (org.geotools.geometry.jts.CompoundCurve) getGeometry(); + } +} diff --git a/src/main/java/org/geoscript/js/geom/Geometry.java b/src/main/java/org/geoscript/js/geom/Geometry.java index 05b5edf6..6349107e 100644 --- a/src/main/java/org/geoscript/js/geom/Geometry.java +++ b/src/main/java/org/geoscript/js/geom/Geometry.java @@ -4,12 +4,18 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import org.locationtech.jts.algorithm.construct.LargestEmptyCircle; +import org.locationtech.jts.algorithm.construct.MaximumInscribedCircle; +import org.locationtech.jts.densify.Densifier; import org.geoscript.js.GeoObject; import org.geoscript.js.proj.Projection; import org.geotools.geometry.jts.GeometryCoordinateSequenceTransformer; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; +import org.locationtech.jts.triangulate.ConformingDelaunayTriangulationBuilder; +import org.locationtech.jts.triangulate.DelaunayTriangulationBuilder; import org.mozilla.javascript.Context; import org.mozilla.javascript.FunctionObject; import org.mozilla.javascript.NativeArray; @@ -27,19 +33,21 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.TransformException; -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.operation.buffer.BufferOp; -import com.vividsolutions.jts.operation.buffer.BufferParameters; -import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.operation.buffer.VariableBuffer; +import org.locationtech.jts.operation.buffer.BufferOp; +import org.locationtech.jts.operation.buffer.BufferParameters; +import org.locationtech.jts.triangulate.VoronoiDiagramBuilder; +import org.locationtech.jts.simplify.DouglasPeuckerSimplifier; public class Geometry extends GeoObject implements Wrapper { /** serialVersionUID */ private static final long serialVersionUID = 8771743870215086281L; - private com.vividsolutions.jts.geom.Geometry geometry; + private org.locationtech.jts.geom.Geometry geometry; protected static GeometryFactory factory = new GeometryFactory(); @@ -51,11 +59,11 @@ public class Geometry extends GeoObject implements Wrapper { public Geometry() { } - com.vividsolutions.jts.geom.Geometry getGeometry() { + org.locationtech.jts.geom.Geometry getGeometry() { return geometry; } - void setGeometry(com.vividsolutions.jts.geom.Geometry geometry) { + void setGeometry(org.locationtech.jts.geom.Geometry geometry) { this.geometry = geometry; } @@ -124,7 +132,7 @@ Object getNativeMethod(String name) { if (binary.contains(name)) { try { - method = geometry.getClass().getMethod(name, com.vividsolutions.jts.geom.Geometry.class); + method = geometry.getClass().getMethod(name, org.locationtech.jts.geom.Geometry.class); } catch (Exception e) { throw new RuntimeException("Unable to find method: " + name, e); } @@ -156,7 +164,7 @@ Object getNativeMethod(String name) { if (constructive1.contains(name)) { try { - method = geometry.getClass().getMethod(name, com.vividsolutions.jts.geom.Geometry.class); + method = geometry.getClass().getMethod(name, org.locationtech.jts.geom.Geometry.class); } catch (Exception e) { throw new RuntimeException("Unable to find method: " + name, e); } @@ -210,9 +218,9 @@ public Geometry transform(Object projObj) { } catch (FactoryException e) { throw new RuntimeException("Failed to find transform.", e); } - com.vividsolutions.jts.geom.Geometry transGeom; + org.locationtech.jts.geom.Geometry transGeom; try { - transGeom = gt.transform((com.vividsolutions.jts.geom.Geometry) this.unwrap()); + transGeom = gt.transform((org.locationtech.jts.geom.Geometry) this.unwrap()); } catch (TransformException e) { throw new RuntimeException("Failed to transform.", e); } @@ -224,7 +232,7 @@ public Geometry transform(Object projObj) { @JSFunction public double distance(Geometry other) { other = sameProjection(this, other); - return geometry.distance((com.vividsolutions.jts.geom.Geometry) other.unwrap()); + return geometry.distance((org.locationtech.jts.geom.Geometry) other.unwrap()); } @JSFunction @@ -244,7 +252,7 @@ public Geometry buffer(double distance, NativeObject options) { params.setEndCapStyle((Integer) capsObj); } } - com.vividsolutions.jts.geom.Geometry buffered = BufferOp.bufferOp(getGeometry(), distance, params); + org.locationtech.jts.geom.Geometry buffered = BufferOp.bufferOp(getGeometry(), distance, params); Geometry wrapped = (Geometry) GeometryWrapper.wrap(getParentScope(), buffered); if (projection != null) { wrapped.projection = projection; @@ -252,6 +260,21 @@ public Geometry buffer(double distance, NativeObject options) { return wrapped; } + @JSFunction + public Geometry variableBuffer(NativeArray distances) { + if (distances.size() == 2) { + return (Geometry) GeometryWrapper.wrap(getParentScope(), VariableBuffer.buffer(getGeometry(), getDouble(distances.get(0)), getDouble(distances.get(1)))); + } else if (distances.size() == 3) { + return (Geometry) GeometryWrapper.wrap(getParentScope(), VariableBuffer.buffer(getGeometry(), getDouble(distances.get(0)), getDouble(distances.get(1)), getDouble(distances.get(2)))); + } else { + return (Geometry) GeometryWrapper.wrap(getParentScope(), VariableBuffer.buffer(getGeometry(), distances.stream().mapToDouble(d -> getDouble(d)).toArray())); + } + } + + private double getDouble(Object obj) { + return ((Number) obj).doubleValue(); + } + @JSGetter public Projection getProjection() { return projection; @@ -286,12 +309,78 @@ public double getArea() { @JSFunction public ScriptableObject simplify(double tolerance) { - com.vividsolutions.jts.geom.Geometry geom = DouglasPeuckerSimplifier.simplify(geometry, tolerance); + org.locationtech.jts.geom.Geometry geom = DouglasPeuckerSimplifier.simplify(geometry, tolerance); ScriptableObject simplified = GeometryWrapper.wrap(getParentScope(), geom); ((Geometry) simplified).projection = projection; return simplified; } - + + @JSFunction + public ScriptableObject densify(double tolerance) { + org.locationtech.jts.geom.Geometry geom = Densifier.densify(geometry, tolerance); + ScriptableObject densified = GeometryWrapper.wrap(getParentScope(), geom); + ((Geometry) densified).projection = projection; + return densified; + } + + @JSFunction + public ScriptableObject createVoronoiDiagram() { + VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder(); + builder.setSites(geometry); + ScriptableObject voronoiDiagram = GeometryWrapper.wrap(getParentScope(), builder.getDiagram(Geometry.factory)); + ((Geometry) voronoiDiagram).projection = projection; + return voronoiDiagram; + } + + @JSFunction + public ScriptableObject randomPoints(int number) { + org.locationtech.jts.shape.random.RandomPointsBuilder builder = new org.locationtech.jts.shape.random.RandomPointsBuilder(factory); + builder.setExtent(geometry); + builder.setNumPoints(number); + org.locationtech.jts.geom.Geometry geom = builder.getGeometry(); + ScriptableObject points = GeometryWrapper.wrap(getParentScope(), geom); + ((Geometry) points).projection = projection; + return points; + } + + @JSFunction + public Geometry getMaximumInscribedCircle(NativeObject config) { + double tolerance = getDouble(config.getOrDefault("tolerance", 1.0)); + MaximumInscribedCircle algorithm = new MaximumInscribedCircle(getGeometry(), tolerance); + return (Geometry) GeometryWrapper.wrap( + getParentScope(), + algorithm.getCenter().buffer(algorithm.getRadiusLine().getLength()) + ); + } + + @JSFunction + public ScriptableObject createDelaunayTriangles(boolean isConforming) { + org.locationtech.jts.geom.Geometry geom; + if (isConforming) { + ConformingDelaunayTriangulationBuilder builder = new ConformingDelaunayTriangulationBuilder(); + builder.setSites(geometry); + geom = builder.getTriangles(factory); + } + else { + DelaunayTriangulationBuilder builder = new DelaunayTriangulationBuilder(); + builder.setSites(geometry); + geom = builder.getTriangles(factory); + } + ScriptableObject triangles = GeometryWrapper.wrap(getParentScope(), geom); + ((Geometry) triangles).projection = projection; + return triangles; + } + + @JSFunction + public Geometry getLargestEmptyCircle(NativeObject config) { + double tolerance = getDouble(config.getOrDefault("tolerance", 1.0)); + LargestEmptyCircle algorithm = new LargestEmptyCircle(getGeometry(), tolerance); + return (Geometry) GeometryWrapper.wrap( + getParentScope(), + algorithm.getCenter().buffer(algorithm.getRadiusLine().getLength()) + ); + } + @JSFunction public String getGeometryType() { return geometry.getGeometryType(); @@ -346,8 +435,8 @@ protected Coordinate[] arrayToCoords(NativeArray array) { Object item = array.get(i); if (item instanceof NativeArray) { coords[i] = arrayToCoord((NativeArray) item); - } else if (item instanceof com.vividsolutions.jts.geom.Point) { - coords[i] = ((com.vividsolutions.jts.geom.Point) item).getCoordinate(); + } else if (item instanceof org.locationtech.jts.geom.Point) { + coords[i] = ((org.locationtech.jts.geom.Point) item).getCoordinate(); } else { throw new RuntimeException("Must provide array of numbers or array of points"); } @@ -482,7 +571,7 @@ public ConstructiveFunction0(String name, Member methodOrConstructor, } @SuppressWarnings("unused") - public com.vividsolutions.jts.geom.Geometry nop() { + public org.locationtech.jts.geom.Geometry nop() { return null; } @@ -496,9 +585,9 @@ public com.vividsolutions.jts.geom.Geometry nop() { public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - com.vividsolutions.jts.geom.Geometry result; + org.locationtech.jts.geom.Geometry result; try { - result = (com.vividsolutions.jts.geom.Geometry) trueMethod.invoke(geometry.unwrap()); + result = (org.locationtech.jts.geom.Geometry) trueMethod.invoke(geometry.unwrap()); } catch (Exception e) { throw new RuntimeException("Failed to invoke method", e); } @@ -524,7 +613,7 @@ public ConstructiveFunction1(String name, Member methodOrConstructor, } @SuppressWarnings("unused") - public com.vividsolutions.jts.geom.Geometry nop(Geometry geometry) { + public org.locationtech.jts.geom.Geometry nop(Geometry geometry) { return null; } @@ -546,9 +635,9 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj, throw new RuntimeException("Must provide a geometry"); } other = sameProjection(geometry, other); - com.vividsolutions.jts.geom.Geometry result; + org.locationtech.jts.geom.Geometry result; try { - result = (com.vividsolutions.jts.geom.Geometry) trueMethod.invoke(geometry.unwrap(), other.unwrap()); + result = (org.locationtech.jts.geom.Geometry) trueMethod.invoke(geometry.unwrap(), other.unwrap()); } catch (Exception e) { throw new RuntimeException("Failed to invoke method", e); } @@ -575,11 +664,11 @@ private Geometry sameProjection(Geometry thisGeom, Geometry otherGeom) { @JSStaticFunction public static Geometry from_(Scriptable geometryObj) { - com.vividsolutions.jts.geom.Geometry geometry = null; + org.locationtech.jts.geom.Geometry geometry = null; if (geometryObj instanceof Wrapper) { Object obj = ((Wrapper) geometryObj).unwrap(); - if (obj instanceof com.vividsolutions.jts.geom.Geometry) { - geometry = (com.vividsolutions.jts.geom.Geometry) obj; + if (obj instanceof org.locationtech.jts.geom.Geometry) { + geometry = (org.locationtech.jts.geom.Geometry) obj; } } if (geometry == null) { @@ -596,4 +685,17 @@ public String toFullString() { return arrayRepr(getCoordinates()); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Geometry geometry1 = (Geometry) o; + return Objects.equals(geometry, geometry1.geometry) && + Objects.equals(projection, geometry1.projection); + } + + @Override + public int hashCode() { + return Objects.hash(geometry, projection); + } } diff --git a/src/main/java/org/geoscript/js/geom/GeometryCollection.java b/src/main/java/org/geoscript/js/geom/GeometryCollection.java index d5de9f66..baa6ea3a 100644 --- a/src/main/java/org/geoscript/js/geom/GeometryCollection.java +++ b/src/main/java/org/geoscript/js/geom/GeometryCollection.java @@ -40,18 +40,18 @@ public GeometryCollection(NativeArray array) { * @param array * @return */ - private com.vividsolutions.jts.geom.GeometryCollection collectionFromArray(NativeArray array) { + private org.locationtech.jts.geom.GeometryCollection collectionFromArray(NativeArray array) { Scriptable scope = array.getParentScope(); Context context = getCurrentContext(); int numComponents = array.size(); - com.vividsolutions.jts.geom.Geometry[] geometries = new com.vividsolutions.jts.geom.Geometry[numComponents]; + org.locationtech.jts.geom.Geometry[] geometries = new org.locationtech.jts.geom.Geometry[numComponents]; for (int i=0; i 2) { @@ -115,14 +115,14 @@ public GeometryCollection(Scriptable scope, NativeObject config) { * Constructor from JTS geometry. * @param geometry */ - public GeometryCollection(Scriptable scope, com.vividsolutions.jts.geom.GeometryCollection geometry) { + public GeometryCollection(Scriptable scope, org.locationtech.jts.geom.GeometryCollection geometry) { this.setParentScope(scope); this.setPrototype(Module.getClassPrototype(GeometryCollection.class)); setGeometry(geometry); } - public com.vividsolutions.jts.geom.GeometryCollection createCollection(com.vividsolutions.jts.geom.Geometry[] geometries) { - return new com.vividsolutions.jts.geom.GeometryCollection(geometries, factory); + public org.locationtech.jts.geom.GeometryCollection createCollection(org.locationtech.jts.geom.Geometry[] geometries) { + return new org.locationtech.jts.geom.GeometryCollection(geometries, factory); } protected int getArrayDimension(NativeArray array) { @@ -178,7 +178,7 @@ protected static NativeArray getCoordinatesArray(Object arg) { public NativeArray getCoordinates() { Context cx = getCurrentContext(); Scriptable scope = getParentScope(); - com.vividsolutions.jts.geom.GeometryCollection geometry = (com.vividsolutions.jts.geom.GeometryCollection) getGeometry(); + org.locationtech.jts.geom.GeometryCollection geometry = (org.locationtech.jts.geom.GeometryCollection) getGeometry(); int length = geometry.getNumGeometries(); NativeArray array = (NativeArray) cx.newArray(scope, length); for (int i=0; i> classes = Arrays.asList( Geometry.class, Point.class, LineString.class, Polygon.class, GeometryCollection.class, MultiPoint.class, - MultiLineString.class, MultiPolygon.class, Bounds.class); + MultiLineString.class, MultiPolygon.class, Bounds.class, + CircularString.class, CompoundCurve.class); prototypes = new HashMap(); for (Class cls : classes) { diff --git a/src/main/java/org/geoscript/js/geom/MultiLineString.java b/src/main/java/org/geoscript/js/geom/MultiLineString.java index c2b078f0..7eceefc9 100644 --- a/src/main/java/org/geoscript/js/geom/MultiLineString.java +++ b/src/main/java/org/geoscript/js/geom/MultiLineString.java @@ -72,15 +72,15 @@ public MultiLineString(Scriptable scope, NativeObject config) { * Constructor from JTS geometry. * @param geometry */ - public MultiLineString(Scriptable scope, com.vividsolutions.jts.geom.MultiLineString geometry) { + public MultiLineString(Scriptable scope, org.locationtech.jts.geom.MultiLineString geometry) { this.setParentScope(scope); this.setPrototype(Module.getClassPrototype(MultiLineString.class)); setGeometry(geometry); } - public com.vividsolutions.jts.geom.MultiLineString createCollection(com.vividsolutions.jts.geom.Geometry[] geometries) { - com.vividsolutions.jts.geom.LineString[] lines = Arrays.copyOf(geometries, geometries.length, com.vividsolutions.jts.geom.LineString[].class); - return new com.vividsolutions.jts.geom.MultiLineString(lines, factory); + public org.locationtech.jts.geom.MultiLineString createCollection(org.locationtech.jts.geom.Geometry[] geometries) { + org.locationtech.jts.geom.LineString[] lines = Arrays.copyOf(geometries, geometries.length, org.locationtech.jts.geom.LineString[].class); + return new org.locationtech.jts.geom.MultiLineString(lines, factory); } /** @@ -114,7 +114,7 @@ public NativeArray getEndPoints() { Scriptable scope = getParentScope(); NativeArray array = (NativeArray) cx.newArray(scope, 2*size); for (int i=0; i prototypes; + + /** + * Define all geometry constructors in the given module scope. If the + * provided scope is not a "top level" scope, constructors will be defined + * in the top level scope for the given scope. + * @param scope + * @throws IllegalAccessException + * @throws InstantiationException + * @throws InvocationTargetException + */ + public static void init(Scriptable scope) throws IllegalAccessException, InstantiationException, InvocationTargetException { + + scope = ScriptableObject.getTopLevelScope(scope); + + @SuppressWarnings("unchecked") + List> classes = Arrays.asList(Quadtree.class, STRtree.class); + + prototypes = new HashMap(); + for (Class cls : classes) { + String name = ScriptableObject.defineClass(scope, cls, false, true); + Scriptable prototype = ScriptableObject.getClassPrototype(scope, name); + prototypes.put(name, prototype); + } + + } + + protected static Scriptable getClassPrototype(Class cls) { + String name = cls.getName(); + if (prototypes == null || !prototypes.containsKey(name)) { + throw new RuntimeException( + "Attempt to access prototype before requiring module: " + name); + } + return prototypes.get(name); + } + +} diff --git a/src/main/java/org/geoscript/js/index/Quadtree.java b/src/main/java/org/geoscript/js/index/Quadtree.java new file mode 100644 index 00000000..14335991 --- /dev/null +++ b/src/main/java/org/geoscript/js/index/Quadtree.java @@ -0,0 +1,66 @@ +package org.geoscript.js.index; + +import org.geoscript.js.geom.Bounds; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; + +public class Quadtree extends SpatialIndex { + + public Quadtree() { + super(new org.locationtech.jts.index.quadtree.Quadtree()); + } + + public Quadtree(Scriptable scope) { + this(); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Quadtree.class)); + } + + + @JSFunction + public void insert(Bounds bounds, Object item) { + this.index.insert(bounds.unwrap(), item); + } + + @JSFunction + public Object query(Bounds bounds) { + return javaToJS(this.index.query(bounds.unwrap()), getParentScope()); + } + + @JSGetter + public int getSize() { + return ((org.locationtech.jts.index.quadtree.Quadtree)this.index).size(); + } + + @JSFunction + public Object queryAll() { + return javaToJS(((org.locationtech.jts.index.quadtree.Quadtree)this.index).queryAll(), getParentScope()); + } + + @JSFunction + public boolean remove(Bounds bounds, Object item) { + return this.index.remove(bounds.unwrap(), item); + } + + /** + * JavaScript constructor. + * @param cx + * @param args + * @param ctorObj + * @param isNewExpr + * @return + */ + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean isNewExpr) { + if (isNewExpr) { + return new Quadtree(); + } else { + return new Quadtree(ctorObj.getParentScope()); + } + } + +} diff --git a/src/main/java/org/geoscript/js/index/STRtree.java b/src/main/java/org/geoscript/js/index/STRtree.java new file mode 100644 index 00000000..dbf4c432 --- /dev/null +++ b/src/main/java/org/geoscript/js/index/STRtree.java @@ -0,0 +1,57 @@ +package org.geoscript.js.index; + +import org.geoscript.js.geom.Bounds; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; + +import java.util.List; + +public class STRtree extends SpatialIndex { + + public STRtree() { + super(new org.locationtech.jts.index.strtree.STRtree()); + } + + public STRtree(Scriptable scope) { + this(); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(STRtree.class)); + } + + @JSFunction + public void insert(Bounds bounds, Object item) { + this.index.insert(bounds.unwrap(), item); + } + + @JSFunction + public List query(Bounds bounds) { + return this.index.query(bounds.unwrap()); + } + + @JSGetter + public int getSize() { + return ((org.locationtech.jts.index.strtree.STRtree)this.index).size(); + } + + /** + * JavaScript constructor. + * @param cx + * @param args + * @param ctorObj + * @param isNewExpr + * @return + */ + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean isNewExpr) { + if (isNewExpr) { + return new STRtree(); + } else { + return new STRtree(ctorObj.getParentScope()); + } + } + +} diff --git a/src/main/java/org/geoscript/js/index/SpatialIndex.java b/src/main/java/org/geoscript/js/index/SpatialIndex.java new file mode 100644 index 00000000..c20b409f --- /dev/null +++ b/src/main/java/org/geoscript/js/index/SpatialIndex.java @@ -0,0 +1,18 @@ +package org.geoscript.js.index; + +import org.geoscript.js.GeoObject; +import org.mozilla.javascript.Wrapper; + +public abstract class SpatialIndex extends GeoObject implements Wrapper { + + protected final org.locationtech.jts.index.SpatialIndex index; + + public SpatialIndex(org.locationtech.jts.index.SpatialIndex index) { + this.index = index; + } + + @Override + public Object unwrap() { + return index; + } +} diff --git a/src/main/java/org/geoscript/js/io/WKT.java b/src/main/java/org/geoscript/js/io/WKT.java index f9b1e1df..64a80b1f 100644 --- a/src/main/java/org/geoscript/js/io/WKT.java +++ b/src/main/java/org/geoscript/js/io/WKT.java @@ -9,13 +9,14 @@ import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; -import com.vividsolutions.jts.io.ParseException; -import com.vividsolutions.jts.io.WKTReader; -import com.vividsolutions.jts.io.WKTWriter; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.geotools.geometry.jts.WKTReader2; +import org.locationtech.jts.io.WKTWriter; public class WKT { - static WKTReader wktReader = new WKTReader(); + static WKTReader wktReader = new WKTReader2(); static WKTWriter wktWriter = new WKTWriter(); /** @@ -39,7 +40,7 @@ public static Geometry read(Context cx, Scriptable thisObj, throw ScriptRuntime.constructError("Error", "The read function expects a single string argument"); } - com.vividsolutions.jts.geom.Geometry jtsGeom = null; + org.locationtech.jts.geom.Geometry jtsGeom = null; try { jtsGeom = wktReader.read(wkt); } catch (ParseException e) { @@ -72,7 +73,7 @@ public static String write(Context cx, Scriptable thisObj, throw ScriptRuntime.constructError("Error", "The write function expects a single geometry argument"); } - return wktWriter.write((com.vividsolutions.jts.geom.Geometry) geometry.unwrap()); + return wktWriter.write((org.locationtech.jts.geom.Geometry) geometry.unwrap()); } /** diff --git a/src/main/java/org/geoscript/js/process/Process.java b/src/main/java/org/geoscript/js/process/Process.java index 4356d5e1..a0748c30 100644 --- a/src/main/java/org/geoscript/js/process/Process.java +++ b/src/main/java/org/geoscript/js/process/Process.java @@ -15,7 +15,7 @@ import org.geotools.process.ProcessException; import org.geotools.process.ProcessFactory; import org.geotools.process.Processors; -import org.geotools.util.NullProgressListener; +import org.geotools.data.util.NullProgressListener; import org.geotools.util.SimpleInternationalString; import org.geotools.util.logging.Logging; import org.mozilla.javascript.Context; diff --git a/src/main/java/org/geoscript/js/proj/Projection.java b/src/main/java/org/geoscript/js/proj/Projection.java index 3f48b250..87371dae 100644 --- a/src/main/java/org/geoscript/js/proj/Projection.java +++ b/src/main/java/org/geoscript/js/proj/Projection.java @@ -1,9 +1,10 @@ package org.geoscript.js.proj; +import java.util.Objects; import java.util.logging.Logger; import org.geoscript.js.GeoObject; -import org.geotools.factory.Hints; +import org.geotools.util.factory.Hints; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.mozilla.javascript.Context; diff --git a/src/main/java/org/geoscript/js/raster/Band.java b/src/main/java/org/geoscript/js/raster/Band.java new file mode 100644 index 00000000..e2b6129e --- /dev/null +++ b/src/main/java/org/geoscript/js/raster/Band.java @@ -0,0 +1,110 @@ +package org.geoscript.js.raster; + +import org.geoscript.js.GeoObject; +import org.geotools.coverage.TypeMap; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Wrapper; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; +import org.opengis.coverage.SampleDimension; + +import javax.measure.Unit; +import java.awt.image.DataBuffer; +import java.util.Arrays; + +public class Band extends GeoObject implements Wrapper { + + private SampleDimension sampleDimension; + + public Band() { + } + + public Band(SampleDimension sampleDimension) { + this.sampleDimension = sampleDimension; + } + + public Band(Scriptable scope, SampleDimension sampleDimension) { + this(sampleDimension); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Band.class)); + } + + @JSGetter + public double getMin() { + return this.sampleDimension.getMinimumValue(); + } + + @JSGetter + public double getMax() { + return this.sampleDimension.getMaximumValue(); + } + + @JSGetter + public Object getNoData() { + return javaToJS(this.sampleDimension.getNoDataValues(), this.getParentScope()); + } + + @JSFunction + public boolean isNoData(double value) { + double[] values = this.sampleDimension.getNoDataValues(); + return Arrays.asList(values).contains(value); + } + + @JSGetter + public double getScale() { + return this.sampleDimension.getScale(); + } + + @JSGetter + public double getOffset() { + return this.sampleDimension.getOffset(); + } + + @JSGetter + public String getType() { + int type = TypeMap.getDataBufferType(this.sampleDimension.getSampleDimensionType()); + if (type == DataBuffer.TYPE_BYTE) { + return "byte"; + } else if (type == DataBuffer.TYPE_DOUBLE) { + return "double"; + } else if (type == DataBuffer.TYPE_FLOAT) { + return "float"; + } else if (type == DataBuffer.TYPE_INT) { + return "int"; + } else if (type == DataBuffer.TYPE_SHORT) { + return "short"; + } else if (type == DataBuffer.TYPE_USHORT) { + return "short"; + } else { + return "undefined"; + } + } + + @JSGetter + public String getDescription() { + return this.sampleDimension.getDescription().toString(); + } + + @Override + public Object unwrap() { + return this.sampleDimension; + } + + @Override + public String toString() { + return getDescription(); + } + + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) { + if (inNewExpr) { + return new Band(null); + } else { + return new Band(ctorObj.getParentScope(), null); + } + } + +} diff --git a/src/main/java/org/geoscript/js/raster/Format.java b/src/main/java/org/geoscript/js/raster/Format.java new file mode 100644 index 00000000..cc186419 --- /dev/null +++ b/src/main/java/org/geoscript/js/raster/Format.java @@ -0,0 +1,225 @@ +package org.geoscript.js.raster; + +import net.miginfocom.layout.Grid; +import org.geoscript.js.GeoObject; +import org.geoscript.js.filter.Expression; +import org.geoscript.js.geom.Bounds; +import org.geoscript.js.proj.Projection; +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.coverage.grid.GridEnvelope2D; +import org.geotools.coverage.grid.GridGeometry2D; +import org.geotools.coverage.grid.io.AbstractGridFormat; +import org.geotools.coverage.grid.io.GridFormatFinder; +import org.geotools.coverage.grid.io.UnknownFormat; +import org.geotools.gce.image.WorldImageFormat; +import org.geotools.util.factory.GeoTools; +import org.geotools.util.factory.Hints; +import org.mozilla.javascript.*; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; +import org.opengis.coverage.grid.GridCoverageReader; +import org.opengis.coverage.grid.GridCoverageWriter; +import org.opengis.parameter.GeneralParameterValue; +import org.opengis.parameter.ParameterValueGroup; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class Format extends GeoObject implements Wrapper { + + private AbstractGridFormat gridFormat; + + private Object source; + + public Format() { + } + + public Format(AbstractGridFormat gridFormat, Object source) { + this.gridFormat = gridFormat; + this.source = source; + } + + public Format(Scriptable scope, AbstractGridFormat gridFormat, Object source) { + this(gridFormat, source); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Format.class)); + } + + @JSGetter + public String getName() { + return gridFormat.getName(); + } + + @JSFunction + public Raster read(Scriptable config) { + Raster raster = new Raster(); + raster.setParentScope(this.getParentScope()); + + Map options = (Map) jsObjectToMap(config); + String name = null; + if (options.containsKey("name")) { + name = (String) options.get("name"); + options.remove(name); + } + + Hints hints = GeoTools.getDefaultHints(); + if (options.containsKey("proj")) { + Projection proj = (Projection) options.get("proj"); + hints.put(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, proj.unwrap()); + } + + GridCoverageReader reader = gridFormat.getReader(source, hints); + try { + if (options.containsKey("bounds")) { + Bounds bounds = (Bounds) options.get("bounds"); + List size = (List) jsToJava(options.get("size")); + options.put("ReadGridGeometry2D", new GridGeometry2D( + new GridEnvelope2D(new Rectangle(size.get(0), size.get((1)))), + bounds.unwrap() + )); + options.remove("bounds"); + options.remove("size"); + } + List values = new ArrayList<>(); + ParameterValueGroup parameterValueGroup = reader.getFormat().getReadParameters(); + for(Map.Entry entry : options.entrySet()) { + parameterValueGroup.parameter(entry.getKey()).setValue(entry.getValue()); + values.add(parameterValueGroup.parameter(entry.getKey())); + } + try { + if (name != null) { + raster = new Raster(this.getParentScope(), (GridCoverage2D) reader.read(name, values.toArray(new GeneralParameterValue[]{}))); + } else { + raster = new Raster(this.getParentScope(), (GridCoverage2D) reader.read(values.toArray(new GeneralParameterValue[]{}))); + } + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Error reading Raster."); + } + } finally { + try { + reader.dispose(); + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Error reading Raster."); + } + } + + return raster; + } + + @JSFunction + public void write(Raster raster, Scriptable config) { + Map options = (Map) jsObjectToMap(config); + GridCoverageWriter writer = gridFormat.getWriter(source); + try { + if (isWorldImage(source.toString())) { + String worldImageFormat = WorldImageFormat.FORMAT.getDefaultValue(); + if (source instanceof File) { + String fileName = ((File)source).getName(); + worldImageFormat = fileName.substring(fileName.lastIndexOf(".") + 1); + } + options.put(WorldImageFormat.FORMAT.getName().toString(), worldImageFormat); + } + List values = new ArrayList<>(); + ParameterValueGroup parameterValueGroup = writer.getFormat().getWriteParameters(); + for(Map.Entry entry : options.entrySet()) { + parameterValueGroup.parameter(entry.getKey()).setValue(entry.getValue()); + values.add(parameterValueGroup.parameter(entry.getKey())); + } + try { + writer.write((GridCoverage2D)raster.unwrap(), values.toArray(new GeneralParameterValue[]{})); + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Can't write Raster." + e.getMessage()); + } + + } finally { + try { + writer.dispose(); + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Error writing Raster."); + } + } + } + + @JSGetter + public NativeArray getNames() { + List names = new ArrayList<>(); + GridCoverageReader reader = this.gridFormat.getReader(source); + try { + try { + names.addAll(Arrays.asList(reader.getGridCoverageNames())); + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Error getting names."); + } + } finally { + try { + reader.dispose(); + } catch (IOException e) { + throw ScriptRuntime.constructError("Error", "Error getting names."); + } + } + return (NativeArray) javaToJS(names, this.getParentScope()); + } + + @Override + public Object unwrap() { + return gridFormat; + } + + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) { + if (args.length == 0) { + return new Format(); + } + Format format = null; + Object arg = args[0]; + Object value = null; + if (arg instanceof String || arg instanceof Number) { + value = arg.toString(); + } else if (arg instanceof NativeObject) { + NativeObject config = (NativeObject) arg; + value = config.get("source", config).toString(); + } else { + throw ScriptRuntime.constructError("Error", "Cannot create Format from provided value: " + Context.toString(ctorObj)); + } + if (value.toString().startsWith("http")) { + try { + value = new URL(value.toString()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } else { + value = new File(value.toString()); + } + + if (inNewExpr) { + try { + if (isWorldImage(value.toString())) { + format = new Format(new WorldImageFormat(), value); + } else { + format = new Format(GridFormatFinder.findFormat(value), value); + } + } catch (Exception e) { + throw ScriptRuntime.constructError("Error", "Cannot create Format from provided value: " + Context.toString(ctorObj)); + } + } else { + format = new Format(ctorObj.getParentScope(), GridFormatFinder.findFormat(value), value); + } + return format; + } + + private static boolean isWorldImage(String fileOrUrl) { + return fileOrUrl.toLowerCase().endsWith("png") || + fileOrUrl.toLowerCase().endsWith("jpg") || + fileOrUrl.toLowerCase().endsWith("jpeg") || + fileOrUrl.toLowerCase().endsWith("gif"); + } + +} diff --git a/src/main/java/org/geoscript/js/raster/Module.java b/src/main/java/org/geoscript/js/raster/Module.java new file mode 100644 index 00000000..0cfbd02c --- /dev/null +++ b/src/main/java/org/geoscript/js/raster/Module.java @@ -0,0 +1,50 @@ +package org.geoscript.js.raster; + +import org.geoscript.js.GeoObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class Module { + + static HashMap prototypes; + + /** + * Define all geometry constructors in the given module scope. If the + * provided scope is not a "top level" scope, constructors will be defined + * in the top level scope for the given scope. + * @param scope + * @throws IllegalAccessException + * @throws InstantiationException + * @throws InvocationTargetException + */ + public static void init(Scriptable scope) throws IllegalAccessException, InstantiationException, InvocationTargetException { + + scope = ScriptableObject.getTopLevelScope(scope); + + @SuppressWarnings("unchecked") + List> classes = Arrays.asList(Band.class, Raster.class, Format.class); + + prototypes = new HashMap(); + for (Class cls : classes) { + String name = ScriptableObject.defineClass(scope, cls, false, true); + Scriptable prototype = ScriptableObject.getClassPrototype(scope, name); + prototypes.put(name, prototype); + } + + } + + protected static Scriptable getClassPrototype(Class cls) { + String name = cls.getName(); + if (prototypes == null || !prototypes.containsKey(name)) { + throw new RuntimeException( + "Attempt to access prototype before requiring module: " + name); + } + return prototypes.get(name); + } + +} diff --git a/src/main/java/org/geoscript/js/raster/Raster.java b/src/main/java/org/geoscript/js/raster/Raster.java new file mode 100644 index 00000000..57e87a96 --- /dev/null +++ b/src/main/java/org/geoscript/js/raster/Raster.java @@ -0,0 +1,306 @@ +package org.geoscript.js.raster; + +import org.geoscript.js.GeoObject; +import org.geoscript.js.geom.Bounds; +import org.geoscript.js.geom.Geometry; +import org.geoscript.js.geom.Point; +import org.geoscript.js.proj.Projection; +import org.geotools.coverage.Category; +import org.geotools.coverage.GridSampleDimension; +import org.geotools.coverage.grid.*; +import org.geotools.coverage.processing.CoverageProcessor; +import org.geotools.geometry.DirectPosition2D; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.process.raster.RangeLookupProcess; +import org.geotools.util.NumberRange; +import org.jaitools.numeric.Range; +import org.mozilla.javascript.*; +import org.mozilla.javascript.annotations.JSConstructor; +import org.mozilla.javascript.annotations.JSFunction; +import org.mozilla.javascript.annotations.JSGetter; +import org.opengis.coverage.SampleDimension; +import org.opengis.geometry.DirectPosition; +import org.opengis.geometry.Envelope; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.TransformException; + +import javax.media.jai.RasterFactory; +import java.awt.*; +import java.awt.image.DataBuffer; +import java.awt.image.WritableRaster; +import java.util.*; +import java.util.List; + +public class Raster extends GeoObject implements Wrapper { + + private GridCoverage2D coverage; + + public Raster() { + // Prototype + } + + public Raster(GridCoverage2D coverage) { + this.coverage = coverage; + } + + public Raster(Scriptable scope, GridCoverage2D coverage) { + this(coverage); + this.setParentScope(scope); + this.setPrototype(Module.getClassPrototype(Raster.class)); + } + + @JSGetter + public String getName() { + return this.coverage.getName().toString(); + } + + @JSGetter + public Projection getProj() { + return new Projection(this.getParentScope(), this.coverage.getCoordinateReferenceSystem2D()); + } + + @JSGetter + public Bounds getBounds() { + Envelope env = coverage.getEnvelope(); + CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem(); + if (crs == null) { + crs = this.coverage.getCoordinateReferenceSystem2D(); + } + double[] l = env.getLowerCorner().getCoordinate(); + double[] u = env.getUpperCorner().getCoordinate(); + ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(l[0], u[0], l[1], u[1], crs); + return new Bounds(this.getParentScope(), referencedEnvelope); + } + + @JSGetter + public NativeArray getSize() { + GridEnvelope2D gridEnvelope2D = coverage.getGridGeometry().getGridRange2D(); + return (NativeArray) javaToJS(Arrays.asList( + (int) gridEnvelope2D.getWidth(), + (int) gridEnvelope2D.getHeight() + ), this.getParentScope()); + } + + @JSGetter + public int getCols() { + return (int) getSize().get(0); + } + + @JSGetter + public int getRows() { + return (int) getSize().get(1); + } + + @JSGetter + public NativeArray getBands() { + List bands = new ArrayList<>(); + for(int i = 0; i coords = new HashMap<>(); + coords.put("coordinates", Arrays.asList(dp.x, dp.y)); + return new Point(this.getParentScope(), (NativeObject) javaToJS(coords, this.getParentScope())); + } catch (TransformException e) { + throw ScriptRuntime.constructError("Error", "Error getting Point from pixel coordinates for Raster."); + } + } + + @JSFunction + public NativeObject getPixel(Point point) { + GridGeometry2D gg = coverage.getGridGeometry(); + try { + GridCoordinates2D gridCoordinates2D = gg.worldToGrid(new DirectPosition2D((double) point.getX(), (double) point.getY())); + Map pixel = new HashMap<>(); + pixel.put("x", gridCoordinates2D.getX()); + pixel.put("y", gridCoordinates2D.getY()); + return (NativeObject) javaToJS(pixel, this.getParentScope()); + } catch (TransformException e) { + throw ScriptRuntime.constructError("Error", "Error getting Pixel coordinate from Point for Raster."); + } + } + + @JSFunction + public Object getValue(Object pointOrPixel) { + Point point; + if (pointOrPixel instanceof Point) { + point = (Point) pointOrPixel; + } else { + NativeObject obj = (NativeObject) pointOrPixel; + int x = (int) obj.get("x", this.getParentScope()); + int y = (int) obj.get("y", this.getParentScope()); + point = getPoint(x, y); + } + DirectPosition dp = new DirectPosition2D(coverage.getCoordinateReferenceSystem2D(), (double) point.getX(), (double) point.getY()); + Object result = coverage.evaluate(dp); + return javaToJS(result, getParentScope()); + } + + @JSFunction + public Raster crop(Object boundsOrGeometry) { + CoverageProcessor processor = new CoverageProcessor(); + ParameterValueGroup params = processor.getOperation("CoverageCrop").getParameters(); + params.parameter("Source").setValue(coverage); + if (boundsOrGeometry instanceof Bounds) { + Bounds bounds = (Bounds) boundsOrGeometry; + params.parameter("Envelope").setValue(new org.geotools.geometry.GeneralEnvelope(bounds.unwrap())); + } else { + Geometry geometry = (Geometry) boundsOrGeometry; + params.parameter("ROI").setValue(geometry.unwrap()); + } + GridCoverage2D newCoverage = (GridCoverage2D) processor.doOperation(params); + return new Raster(this.getParentScope(), newCoverage); + } + + @JSFunction + public Raster reproject(Projection projection) { + CoverageProcessor processor = new CoverageProcessor(); + ParameterValueGroup params = processor.getOperation("Resample").getParameters(); + params.parameter("Source").setValue(coverage); + params.parameter("CoordinateReferenceSystem").setValue(projection.unwrap()); + GridCoverage2D newCoverage = (GridCoverage2D) processor.doOperation(params); + return new Raster(this.getParentScope(), newCoverage); + } + + @JSFunction + public Raster reclassify(NativeArray ranges, NativeObject options) { + int band = (int) options.getOrDefault("band", 0); + double noData = (double) options.getOrDefault("noData",0); + List rangeList = new ArrayList<>(); + int[] pixelValues = new int[ranges.size()]; + for(int i = 0; i values = new HashMap<>(); + values.put("min", coverage.getProperty("minimum")); + values.put("max",coverage.getProperty("maximum")); + return (NativeObject) javaToJS(values, this.getParentScope()); + } + + @JSFunction + public Object getMinValue(int band) { + double minValue = this.coverage.getSampleDimension(band).getMinimumValue(); + if (Double.isInfinite(minValue)) { + minValue = ((double[])this.getExtrema().get("min"))[band]; + } + return minValue; + } + + @JSFunction + public Object getMaxValue(int band) { + double maxValue = this.coverage.getSampleDimension(band).getMaximumValue(); + if (Double.isInfinite(maxValue)) { + maxValue = ((double[])this.getExtrema().get("max"))[band]; + } + return maxValue; + } + + @JSGetter + public NativeArray getBlockSize() { + int[] size = this.coverage.getOptimalDataBlockSizes(); + return (NativeArray) javaToJS(Arrays.asList( + size[0], + size[1] + ), this.getParentScope()); + } + + @JSGetter + public NativeArray getPixelSize() { + Bounds bounds = this.getBounds(); + NativeArray size = this.getSize(); + return (NativeArray) javaToJS(Arrays.asList( + ((double) bounds.getWidth()) / ((int)size.get(0)), + ((double) bounds.getHeight()) / ((int)size.get(1)) + ), this.getParentScope()); + } + + private int getInt(Object obj) { + if (obj instanceof Number) { + return ((Number)obj).intValue(); + } else { + return getInt(Double.parseDouble(obj.toString())); + } + } + + @Override + public String toString() { + return this.getName(); + } + + @Override + public Object unwrap() { + return coverage; + } + + @JSConstructor + public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) { + NativeArray data = (NativeArray) args[0]; + Bounds bounds = (Bounds) args[1]; + + double min = Double.MAX_VALUE; + double max = Double.MIN_VALUE; + float[][] matrix = new float[(int) data.getLength()][ (int) (data.getLength() > 0 ? ((NativeArray) data.get(0)).getLength() : 0)]; + for(int i = 0; i max) { + max = value; + } + matrix[i][j] = value; + } + } + + int width = matrix[0].length; + int height = matrix.length; + + WritableRaster writableRaster = RasterFactory.createBandedRaster(DataBuffer.TYPE_FLOAT, width, height, 1, null); + for(int i = 0; i g.coordinates[0].length, "densified line has more coordinates"); + ASSERT.ok(g.projection.equals(g.projection), "same projection"); +}; + exports["test: bounds"] = function() { diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_format.js b/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_format.js new file mode 100644 index 00000000..822195a8 --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_format.js @@ -0,0 +1,33 @@ +var assert = require("assert"); +var admin = require("../../admin"); +var fs = require("fs"); + +var raster = require("geoscript/raster"); +var proj = require('geoscript/proj'); + +exports["test: format should get name"] = function() { + var format = new raster.Format({source: admin.raster.source}); + assert.strictEqual(format.name, "GeoTIFF", "Name should be GeoTIFF"); +}; + +exports["test: format should read a raster"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.ok(tif instanceof raster.Raster, "instance should be Raster"); +}; + +exports["test: format should write a raster"] = function() { + var readFormat = new raster.Format({source: admin.raster.source}); + var readTif = readFormat.read({}); + var writeFormat = new raster.Format({source: admin.raster.writePng}); + writeFormat.write(readTif, {}); + var writePng = writeFormat.read({}); + assert.ok(writePng instanceof raster.Raster, "instance should be Raster"); +}; + +exports["test: format should get names from a raster"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var names = format.names; + assert.strictEqual(1, names.length, "should only have one name"); + assert.strictEqual("raster", names[0], "first and only name should be raster"); +}; \ No newline at end of file diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_raster.js b/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_raster.js new file mode 100644 index 00000000..d83a58ed --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/raster/test_raster.js @@ -0,0 +1,172 @@ +var assert = require("assert"); +var admin = require("../../admin"); + +var raster = require("geoscript/raster"); +var geom = require('geoscript/geom'); +var proj = require('geoscript/proj'); + +exports["test: create a raster from data"] = function() { + var ras = new raster.Raster([ + [1,1,1,1,1], + [1,2,2,2,1], + [1,2,3,2,1] + ], new geom.Bounds([0,0,10,10])) + assert.ok(ras instanceof raster.Raster, "instance should be Raster"); + assert.strictEqual(1, ras.getMinValue(), "Min value should be 1"); + assert.strictEqual(3, ras.getMaxValue(), "Max value should be 3"); +}; + +exports["test: read a raster"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.ok(tif instanceof raster.Raster, "instance should be Raster"); +}; + +exports["test: get raster name"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.strictEqual("raster", tif.name, "Name should be raster"); +}; + +exports["test: get raster projection"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.strictEqual("EPSG:4326", tif.proj.id, "Projection should be EPSG:4326"); +}; + +exports["test: get raster bounds"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var bounds = tif.bounds + assert.strictEqual(-180, Math.round(bounds.minX), "Min X should be -180"); + assert.strictEqual(-90, Math.round(bounds.minY), "Min Y should be -90"); + assert.strictEqual(180, Math.round(bounds.maxX), "Max X should be 180"); + assert.strictEqual(90, Math.round(bounds.maxY), "Max Y should be 90"); +}; + +exports["test: get raster size"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var size = tif.size + assert.strictEqual(2, size.length, "Size should be an array with two entries (width and height)"); + assert.strictEqual(900, size[0], "Width should be 900"); + assert.strictEqual(450, size[1], "Height should be 450"); +}; + +exports["test: get raster columns and rows"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.strictEqual(900, tif.cols, "Columns should be 900"); + assert.strictEqual(450, tif.rows, "Rows should be 450"); +}; + +exports["test: get raster bands"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var bands = tif.bands; + assert.strictEqual(1, bands.length, "There should be 1 band"); + assert.strictEqual(-Infinity, bands[0].min, "Get band min"); + assert.strictEqual(Infinity, bands[0].max, "Get band max"); + assert.strictEqual(undefined, bands[0].nodata, "Get band no data"); + assert.strictEqual(1, bands[0].scale, "Get band scale"); + assert.strictEqual(0, bands[0].offset, "Get band offset"); + assert.strictEqual("byte", bands[0].type, "Get band type"); + assert.strictEqual("GRAY_INDEX", bands[0].description, "Get band description"); +}; + +exports["test: get raster point from pixel"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var pt = tif.getPoint(10,20); + assert.strictEqual("-175.8", pt.x.toFixed(1), "Point x should be -175.8"); + assert.strictEqual("81.8", pt.y.toFixed(1), "Point y should be 81.8"); +}; + +exports["test: get raster point from pixel"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.strictEqual(-69, tif.getValue(new geom.Point([-175.8, 81.8]), "double")[0], "Value should be -69"); + assert.strictEqual(-69, tif.getValue({x: 10, y: 20}, "double")[0], "Value should be -69"); +}; + +exports["test: get raster pixel from point"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var pixel = tif.getPixel(new geom.Point([-175.8, 81.8])); + assert.strictEqual(10, pixel.x, "Value should be 10"); + assert.strictEqual(20, pixel.y, "Value should be 20"); +}; + +exports["test: crop a raster with a bounds"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var smallTif = tif.crop(new geom.Bounds([-180,-90, 0, 0])) + var bounds = smallTif.bounds; + assert.strictEqual(-180, Math.round(bounds.minX), "Min X should be -180"); + assert.strictEqual(-90, Math.round(bounds.minY), "Min Y should be -90"); + assert.strictEqual(0, Math.round(bounds.maxX), "Max X should be 0"); + assert.strictEqual(0, Math.round(bounds.maxY), "Max Y should be 0"); +}; + +exports["test: crop a raster with a geometry"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var smallTif = tif.crop(new geom.Point([0, 0]).buffer(4)) + var bounds = smallTif.bounds; + assert.strictEqual(-4, Math.round(bounds.minX), "Min X should be -4"); + assert.strictEqual(-4, Math.round(bounds.minY), "Min Y should be -4"); + assert.strictEqual(4, Math.round(bounds.maxX), "Max X should be 4"); + assert.strictEqual(4, Math.round(bounds.maxY), "Max Y should be 4"); +}; + +exports["test: reproject a raster"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}).crop(new geom.Point([0, 0]).buffer(4)); + var reprojectedTif = tif.reproject(new proj.Projection("EPSG:3857")); + assert.strictEqual("EPSG:4326", tif.proj.id, "Original raster should be EPSG:4326"); + assert.strictEqual("EPSG:3857", reprojectedTif.proj.id, "Original raster should be EPSG:3857"); +}; + +exports["test: reclassify a raster"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var reclassifiedTif = tif.reclassify([ + {min: -1000, max: 0, value: -1}, + {min: 0, max: 50, value: 1}, + {min: 50, max: 100, value: 2}, + {min: 100, max: 2000, value: 3} + ], {noData: 0}); + assert.strictEqual(-1, reclassifiedTif.getValue(new geom.Point([-175.8, 81.8]), "double")[0], "Value should be -1"); + assert.strictEqual(-1, reclassifiedTif.getValue({x: 10, y: 20}, "double")[0], "Value should be -1"); +}; + +exports["test: get min and max values for a raster band"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + assert.strictEqual(49, tif.getMinValue(0)); + assert.strictEqual(255, tif.getMaxValue(0)); +}; + +exports["test: get block size"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var blockSize = tif.blockSize; + assert.strictEqual(900, blockSize[0], "Block Size Width should be 900"); + assert.strictEqual(9, blockSize[1], "Block Size Height should be 9 "); +}; + +exports["test: get pixel size"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var pixelSize = tif.pixelSize; + assert.strictEqual(0.4, pixelSize[0], "Pixel Size Width should be 0.4"); + assert.strictEqual(0.4, pixelSize[1], "Pixel Size Height should be 0.4"); +}; + +exports["test: get extrema for all raster bands"] = function() { + var format = new raster.Format({source: admin.raster.source}); + var tif = format.read({}); + var extrema = tif.extrema + assert.strictEqual(49, extrema.min[0]); + assert.strictEqual(255, extrema.max[0]); +}; \ No newline at end of file diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/style/test_shape.js b/src/test/resources/org/geoscript/js/tests/geoscript/style/test_shape.js index 58592e4b..1389bd13 100644 --- a/src/test/resources/org/geoscript/js/tests/geoscript/style/test_shape.js +++ b/src/test/resources/org/geoscript/js/tests/geoscript/style/test_shape.js @@ -104,14 +104,14 @@ exports["test: rotation"] = function() { shape = new STYLE.Shape({rotation: 30}); ASSERT.ok(shape.rotation instanceof Expression, "rotation is expression"); ASSERT.ok(shape.rotation.literal, "rotation is literal expression"); - ASSERT.strictEqual(shape.rotation.text, "30", "correct rotation"); + ASSERT.strictEqual(shape.rotation.text, "30.0", "correct rotation"); // literal for rotation shape = new STYLE.Shape(); shape.rotation = 90; ASSERT.ok(shape.rotation instanceof Expression, "rotation is expression"); ASSERT.ok(shape.rotation.literal, "rotation is literal expression"); - ASSERT.strictEqual(shape.rotation.text, "90", "rotation from expression"); + ASSERT.strictEqual(shape.rotation.text, "90.0", "rotation from expression"); // expression for rotation shape = new STYLE.Shape(); diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/style/test_style.js b/src/test/resources/org/geoscript/js/tests/geoscript/style/test_style.js index 00cbed28..3fdade0b 100644 --- a/src/test/resources/org/geoscript/js/tests/geoscript/style/test_style.js +++ b/src/test/resources/org/geoscript/js/tests/geoscript/style/test_style.js @@ -115,10 +115,10 @@ exports["test: _style (simple)"] = function() { var featureTypeStyles = _style.featureTypeStyles(); ASSERT.equal(featureTypeStyles.size(), 1, "one feature type style"); - var rules = featureTypeStyles.get(0).getRules(); - ASSERT.equal(rules.length, 2, "two rules"); + var rules = featureTypeStyles.get(0).rules(); + ASSERT.equal(rules.size(), 2, "two rules " + rules); - var rule = rules[0]; + var rule = rules.get(0); ASSERT.ok(rule instanceof geotools.styling.Rule, "rule is correct type"); var symbolizers = rule.getSymbolizers(); @@ -129,11 +129,11 @@ exports["test: _style (simple)"] = function() { var graphic = symbolizer.getGraphic(); ASSERT.ok(graphic instanceof geotools.styling.GraphicImpl, "correct graphic type for first symbolizer"); - ASSERT.equal(graphic.getSymbols()[0].getWellKnownName(), "circle", "correct graphic name for first symbolizer"); + ASSERT.equal(graphic.graphicalSymbols().get(0).getWellKnownName(), "circle", "correct graphic name for first symbolizer"); ASSERT.equal(graphic.getSize(), 12, "correct graphic size for first symbolizer") - ASSERT.equal(graphic.getSymbols()[0].getFill().getColor(), "#ff0000", "correct fill color for first symbolizer") + ASSERT.equal(graphic.graphicalSymbols().get(0).getFill().getColor(), "#ff0000", "correct fill color for first symbolizer") - var rule = rules[1]; + var rule = rules.get(1); ASSERT.ok(rule instanceof geotools.styling.Rule, "rule is correct type"); var symbolizers = rule.getSymbolizers(); @@ -144,9 +144,9 @@ exports["test: _style (simple)"] = function() { var graphic = symbolizer.getGraphic(); ASSERT.ok(graphic instanceof geotools.styling.GraphicImpl, "correct graphic type for second symbolizer"); - ASSERT.equal(graphic.getSymbols()[0].getWellKnownName(), "star", "correct graphic name for second symbolizer"); + ASSERT.equal(graphic.graphicalSymbols().get(0).getWellKnownName(), "star", "correct graphic name for second symbolizer"); ASSERT.equal(graphic.getSize(), 11, "correct graphic size for second symbolizer") - ASSERT.equal(graphic.getSymbols()[0].getFill().getColor(), "#ffffff", "correct fill color for second symbolizer") + ASSERT.equal(graphic.graphicalSymbols().get(0).getFill().getColor(), "#ffffff", "correct fill color for second symbolizer") }; @@ -176,15 +176,15 @@ exports["test: _style (multiple featureTypeStyle)"] = function() { var featureTypeStyles = _style.featureTypeStyles(); ASSERT.equal(featureTypeStyles.size(), 2, "two feature type styles"); - var rules = featureTypeStyles.get(0).getRules(); - ASSERT.equal(rules.length, 1, "one rule in first feature type style"); + var rules = featureTypeStyles.get(0).rules(); + ASSERT.equal(rules.size(), 1, "one rule in first feature type style"); - var rule = rules[0]; + var rule = rules.get(0); ASSERT.ok(rule instanceof geotools.styling.Rule, "rule is correct type in first feature type style"); ASSERT.equal(rule.getMinScaleDenominator(), 100000, "first rule has correct min scale denominator"); ASSERT.equal(rule.getMaxScaleDenominator(), 200000, "first rule has correct max scale denominator"); ASSERT.ok(rule.getFilter() instanceof geotools.filter.IsEqualsToImpl, "first rule has filter"); - ASSERT.equal(String(rule.getFilter().getRightValue()), "foo", "first rule filter is good"); + ASSERT.equal(String(rule.getFilter().getExpression2().getValue()), "foo", "first rule filter is good"); var symbolizers = rule.getSymbolizers(); ASSERT.equal(symbolizers.length, 1, "one symbolizer in first feature type style"); @@ -197,10 +197,10 @@ exports["test: _style (multiple featureTypeStyle)"] = function() { ASSERT.equal(stroke.getWidth(), 5, "correct stroke width for first symbolizer in first feature type style"); ASSERT.equal(stroke.getColor(), "#ffff00", "correct stroke color for first symbolizer in first feature type style") - var rules = featureTypeStyles.get(1).getRules(); - ASSERT.equal(rules.length, 1, "one rule in second feature type style"); + var rules = featureTypeStyles.get(1).rules(); + ASSERT.equal(rules.size(), 1, "one rule in second feature type style"); - var rule = rules[0]; + var rule = rules.get(0); ASSERT.ok(rule instanceof geotools.styling.Rule, "rule is correct type in second feature type style"); ASSERT.equal(rule.getMinScaleDenominator(), 0, "second rule has correct min scale denominator"); ASSERT.equal(rule.getMaxScaleDenominator(), Infinity, "second rule has correct max scale denominator"); diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/test_filter.js b/src/test/resources/org/geoscript/js/tests/geoscript/test_filter.js index 68b5a87e..44e2643a 100644 --- a/src/test/resources/org/geoscript/js/tests/geoscript/test_filter.js +++ b/src/test/resources/org/geoscript/js/tests/geoscript/test_filter.js @@ -5,14 +5,14 @@ var FILTER = require("geoscript/filter"); exports["test and"] = function() { var f = FILTER.and(["name = 'foo'", "type = 'bar'"]); - ASSERT.strictEqual(f.cql, "(name = 'foo' AND type = 'bar')", "correct cql"); + ASSERT.strictEqual(f.cql, "name = 'foo' AND type = 'bar'", "correct cql"); }; exports["test or"] = function() { var f = FILTER.or(["name = 'foo'", "type = 'bar'"]); - ASSERT.strictEqual(f.cql, "(name = 'foo' OR type = 'bar')", "correct cql"); + ASSERT.strictEqual(f.cql, "name = 'foo' OR type = 'bar'", "correct cql"); }; diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/test_geom.js b/src/test/resources/org/geoscript/js/tests/geoscript/test_geom.js index 0f013189..25db0db9 100644 --- a/src/test/resources/org/geoscript/js/tests/geoscript/test_geom.js +++ b/src/test/resources/org/geoscript/js/tests/geoscript/test_geom.js @@ -22,7 +22,7 @@ exports["test: create(point)"] = function() { exports["test: create(linestring)"] = function() { var type = "LineString"; - var coordinates = [[0, 1], [1, 2]]; + var coordinates = [[0, 1], [1, 2]]; var o, g; // create a linestring @@ -157,12 +157,95 @@ exports["test: create(bounds)"] = function() { }; +exports["test: create voronoi diagram"] = function() { + + var geom = GEOM.Point([1,1]).buffer(50) + var points = geom.randomPoints(20) + var diagram = points.createVoronoiDiagram() + ASSERT.ok(diagram instanceof GEOM.GeometryCollection) + ASSERT.ok(diagram.components.length > 0, "there should be more than 0 polygons"); + +} + +exports["test: create random points"] = function() { + + var geom = GEOM.Point([1,1]).buffer(10) + var points = geom.randomPoints(10) + ASSERT.ok(points instanceof GEOM.MultiPoint) + ASSERT.strictEqual(points.components.length, 10, "geometry has 10 components"); + +} + +exports["test: largest empty circle"] = function() { + + var geom = new GEOM.Polygon([[ + [-122.38855361938475, 47.5805786829606], [-122.38636493682861, 47.5783206388176], + [-122.38700866699219, 47.5750491969984], [-122.38177299499512, 47.57502024527343], + [-122.38481998443604, 47.5780600889959], [-122.38151550292969, 47.5805786829606], + [-122.38855361938475, 47.5805786829606] + ]]); + ASSERT.ok(geom instanceof GEOM.Polygon); + var circle = geom.getLargestEmptyCircle({tolerance: 1.0}); + ASSERT.ok(circle instanceof GEOM.Polygon); + +} + +exports["test: create conforming delaunay triangles"] = function() { + + var geom = GEOM.Point([1,1]).buffer(50) + var points = geom.randomPoints(20) + var triangles = points.createDelaunayTriangles(true) + ASSERT.ok(triangles instanceof GEOM.GeometryCollection) + ASSERT.ok(triangles.components.length > 0, "there should be more than 0 triangles"); + +} + +exports["test: maximum inscribed circle"] = function() { + + var geom = new GEOM.Polygon([[ + [-122.38855361938475, 47.5805786829606], [-122.38636493682861, 47.5783206388176], + [-122.38700866699219, 47.5750491969984], [-122.38177299499512, 47.57502024527343], + [-122.38481998443604, 47.5780600889959], [-122.38151550292969, 47.5805786829606], + [-122.38855361938475, 47.5805786829606] + ]]); + ASSERT.ok(geom instanceof GEOM.Polygon); + var circle = geom.getMaximumInscribedCircle({tolerance: 1.0}); + ASSERT.ok(circle instanceof GEOM.Polygon); + +} + +exports["test: create non-conforming delaunay triangles"] = function() { + + var geom = GEOM.Point([1,1]).buffer(50) + var points = geom.randomPoints(20) + var triangles = points.createDelaunayTriangles(false) + ASSERT.ok(triangles instanceof GEOM.GeometryCollection) + ASSERT.ok(triangles.components.length > 0, "there should be more than 0 triangles"); + +} + +exports["test: variable buffer"] = function() { + + var geom = new GEOM.LineString([[1,2], [10,20], [30,50], [100, 150]]); + var buffer = geom.variableBuffer([10,50]) + ASSERT.ok(buffer instanceof GEOM.Polygon) + + buffer = geom.variableBuffer([10, 20, 50]) + ASSERT.ok(buffer instanceof GEOM.Polygon) + + buffer = geom.variableBuffer([10, 20, 50, 75]) + ASSERT.ok(buffer instanceof GEOM.Polygon) + +} + exports["test: Point"] = require("./geom/test_point"); exports["test: LineString"] = require("./geom/test_linestring"); exports["test: Polygon"] = require("./geom/test_polygon"); exports["test: Collection"] = require("./geom/test_collection"); exports["test: MultiLineString"] = require("./geom/test_multilinestring"); exports["test: Bounds"] = require("./geom/test_bounds"); +exports["test: CircularString"] = require("./geom/test_circularstring"); +exports["test: CompoundCurve"] = require("./geom/test_compoundcurve"); if (require.main == module.id) { system.exit(require("test").run(exports)); diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/test_index.js b/src/test/resources/org/geoscript/js/tests/geoscript/test_index.js new file mode 100644 index 00000000..154a844e --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/test_index.js @@ -0,0 +1,37 @@ +var assert = require("assert"); +var index = require("geoscript/index"); +var geom = require("geoscript/geom"); + +exports["test: quadtree index"] = function() { + + var quadtree = new index.QuadTree(); + quadtree.insert(new geom.Bounds([0,0,10,10]), new geom.Point([5,5])); + quadtree.insert(new geom.Bounds([2,2,6,6]), new geom.Point([4,4])); + quadtree.insert(new geom.Bounds([20,20,60,60]), new geom.Point([30,30])); + quadtree.insert(new geom.Bounds([22,22,44,44]), new geom.Point([32,32])); + assert.strictEqual(4, quadtree.size, "QuadTree index should have 4 entries"); + + var results = quadtree.query(new geom.Bounds([1,1,5,5])); + assert.strictEqual(4, results.length); + + var allResults = quadtree.queryAll(); + assert.strictEqual(4, allResults.length); + + var isRemoved = quadtree.remove(new geom.Bounds([22,22,44,44]), new geom.Point([32,32])); + assert.ok(isRemoved) + + allResults = quadtree.queryAll() + assert.strictEqual(3, allResults.length); + +}; + +exports["test: strtree index"] = function() { + + var strtree = new index.STRtree(); + strtree.insert(new geom.Bounds([0,0,10,10]), new geom.Point([5,5])); + strtree.insert(new geom.Bounds([2,2,6,6]), new geom.Point([4,4])); + strtree.insert(new geom.Bounds([20,20,60,60]), new geom.Point([30,30])); + strtree.insert(new geom.Bounds([22,22,44,44]), new geom.Point([32,32])); + assert.strictEqual(4, strtree.size, "QuadTree index should have 4 entries"); + +}; \ No newline at end of file diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/test_raster.js b/src/test/resources/org/geoscript/js/tests/geoscript/test_raster.js new file mode 100644 index 00000000..22abcba4 --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/test_raster.js @@ -0,0 +1,6 @@ +exports["test: Format"] = require("./raster/test_format"); +exports["test: Raster"] = require("./raster/test_raster"); + +if (require.main == module.id) { + system.exit(require("test").run(exports)); +} diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/test_workspace.js b/src/test/resources/org/geoscript/js/tests/geoscript/test_workspace.js index 5f5914b2..e5ac44f2 100644 --- a/src/test/resources/org/geoscript/js/tests/geoscript/test_workspace.js +++ b/src/test/resources/org/geoscript/js/tests/geoscript/test_workspace.js @@ -34,6 +34,9 @@ exports["test: Workspace.from_"] = function() { exports["test: Directory"] = require("./workspace/test_directory"); exports["test: H2"] = require("./workspace/test_h2"); +exports["test: GeoPackage"] = require("./workspace/test_geopackage"); +exports["test: Geobuf"] = require("./workspace/test_geobuf"); +exports["test: Flatgeobuf"] = require("./workspace/test_flatgeobuf"); exports["test: Memory"] = require("./workspace/test_memory"); // exports["test: PostGIS"] = require("./workspace/test_postgis"); diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_flatgeobuf.js b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_flatgeobuf.js new file mode 100644 index 00000000..cd6fd7fe --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_flatgeobuf.js @@ -0,0 +1,45 @@ +var ASSERT = require("assert"); +var WORKSPACE = require("geoscript/workspace"); +var LAYER = require("geoscript/layer"); +var GEOM = require("geoscript/geom"); +var FS = require("fs"); + +exports["test: constructor"] = function() { + + var Files = Packages.java.nio.file.Files; + var file = Files.createTempDirectory("flatgeobuf").toFile().getAbsolutePath(); + var geobuf = new WORKSPACE.Flatgeobuf({file: file}); + + ASSERT.ok(geobuf instanceof WORKSPACE.Workspace, "instanceof Workspace"); + ASSERT.ok(geobuf instanceof WORKSPACE.Flatgeobuf, "instanceof Flatgeobuf"); + + geobuf.close(); + +}; + +exports["test: create"] = function() { + + var Files = Packages.java.nio.file.Files; + var file = Files.createTempDirectory("flatgeobuf").toFile().getAbsolutePath(); + var geobuf = new WORKSPACE.Flatgeobuf({file: file}); + + var layer = new LAYER.Layer({ + name: "places", + fields: [{ + name: "name", type: "String" + }, { + name: "geom", type: "Point" + }] + }); + var geobufLayer = geobuf.add(layer); + + geobufLayer.add({name: "San Francisco", geom: new GEOM.Point([-122.42, 37.78])}); + geobufLayer.add({name: "New York", geom: new GEOM.Point([-73.58, 40.47])}); + ASSERT.ok(geobufLayer.count == 2); + + geobuf.close(); +}; + +if (require.main == module.id) { + system.exit(require("test").run(exports)); +} diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geobuf.js b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geobuf.js new file mode 100644 index 00000000..790f3bae --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geobuf.js @@ -0,0 +1,45 @@ +var ASSERT = require("assert"); +var WORKSPACE = require("geoscript/workspace"); +var LAYER = require("geoscript/layer"); +var GEOM = require("geoscript/geom"); +var FS = require("fs"); + +exports["test: constructor"] = function() { + + var Files = Packages.java.nio.file.Files; + var file = Files.createTempDirectory("geobuf").toFile().getAbsolutePath(); + var geobuf = new WORKSPACE.Geobuf({file: file}); + + ASSERT.ok(geobuf instanceof WORKSPACE.Workspace, "instanceof Workspace"); + ASSERT.ok(geobuf instanceof WORKSPACE.Geobuf, "instanceof Geobuf"); + + geobuf.close(); + +}; + +exports["test: create"] = function() { + + var Files = Packages.java.nio.file.Files; + var file = Files.createTempDirectory("geobuf").toFile().getAbsolutePath(); + var geobuf = new WORKSPACE.Geobuf({file: file}); + + var layer = new LAYER.Layer({ + name: "cities", + fields: [{ + name: "name", type: "String" + }, { + name: "geom", type: "Point" + }] + }); + var geobufLayer = geobuf.add(layer); + + geobufLayer.add({name: "San Francisco", geom: new GEOM.Point([-122.42, 37.78])}); + geobufLayer.add({name: "New York", geom: new GEOM.Point([-73.58, 40.47])}); + ASSERT.ok(geobufLayer.count == 2); + + geobuf.close(); +}; + +if (require.main == module.id) { + system.exit(require("test").run(exports)); +} diff --git a/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geopackage.js b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geopackage.js new file mode 100644 index 00000000..6d849ccd --- /dev/null +++ b/src/test/resources/org/geoscript/js/tests/geoscript/workspace/test_geopackage.js @@ -0,0 +1,45 @@ +var ASSERT = require("assert"); +var WORKSPACE = require("geoscript/workspace"); +var LAYER = require("geoscript/layer"); +var FS = require("fs"); +var admin = require("../../admin"); + +var database = FS.join(admin.geopkg.dest, "geoscript.gpkg"); +exports.setUp = admin.geopkg.setUp; +exports.tearDown = admin.geopkg.tearDown; + +exports["test: constructor"] = function() { + + var geopkg = new WORKSPACE.GeoPackage(); + + ASSERT.ok(geopkg instanceof WORKSPACE.Workspace, "instanceof Workspace"); + ASSERT.ok(geopkg instanceof WORKSPACE.GeoPackage, "instanceof GeoPackage"); + + geopkg.close(); + +}; + +exports["test: names"] = function() { + + var geopkg = new WORKSPACE.GeoPackage({database: database}); + + ASSERT.ok(geopkg.names.indexOf("states") > -1, "geopkg.names includes 'states'"); + + geopkg.close(); + +}; + +exports["test: get"] = function() { + + var geopkg = new WORKSPACE.GeoPackage({database: database}); + + var states = geopkg.get("states"); + ASSERT.ok(states instanceof LAYER.Layer, "get returns a layer instance"); + + geopkg.close(); + +}; + +if (require.main == module.id) { + system.exit(require("test").run(exports)); +} diff --git a/src/test/resources/org/geoscript/js/tests/test_geoscript.js b/src/test/resources/org/geoscript/js/tests/test_geoscript.js index 259ecb53..1af18cba 100644 --- a/src/test/resources/org/geoscript/js/tests/test_geoscript.js +++ b/src/test/resources/org/geoscript/js/tests/test_geoscript.js @@ -8,6 +8,8 @@ exports["test: workspace"] = require("./geoscript/test_workspace"); exports["test: style"] = require("./geoscript/test_style"); exports["test: map"] = require("./geoscript/test_map"); exports["test: util"] = require("./geoscript/test_util"); +exports["test: index"] = require("./geoscript/test_index"); +exports["test: raster"] = require("./geoscript/test_raster"); if (require.main == module.id) { system.exit(require("test").run(exports));