-
Notifications
You must be signed in to change notification settings - Fork 434
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GEOMESA-3408 Document feature-to-feature converter (#3230)
- Loading branch information
1 parent
c7e71b7
commit 5cedfd1
Showing
4 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
Feature-To-Feature Converter | ||
============================ | ||
|
||
The feature-to-feature converter can be used to transform ``SimpleFeature``\ s from one ``SimpleFeatureType`` to another. | ||
Unlike other GeoMesa converters, the feature-to-feature converter must be invoked programmatically, as there is no | ||
native decoding of features from an input stream. | ||
|
||
Configuration | ||
------------- | ||
|
||
The feature-to-feature converter expects a ``type`` of ``simple-feature``. It also requires the input feature type to be | ||
defined with ``input-sft``, which must reference the name of a feature type available on the classpath - see | ||
:ref:`converter_sft_defs` for details on making the feature type available. | ||
|
||
The ``fields`` of the converter can reference the attributes of the input feature type by name, using ``$`` notation. Any | ||
fields that have the same name as the input type will be automatically copied, unless they are explicitly redefined in the | ||
converter definition. The feature ID will also be copied, unless it is redefined with ``id-field``. | ||
|
||
Example Usage | ||
------------- | ||
|
||
Given an input feature type defined as: | ||
|
||
:: | ||
|
||
geomesa.sfts.intype = { | ||
type-name = "intype" | ||
attributes = [ | ||
{ name = "number", type = "Integer" } | ||
{ name = "color", type = "String" } | ||
{ name = "weight", type = "Double" } | ||
{ name = "geom", type = "Point" } | ||
] | ||
} | ||
|
||
And an output feature type defined as: | ||
|
||
:: | ||
|
||
geomesa.sfts.outtype = { | ||
type-name = "outtype" | ||
attributes = [ | ||
{ name = "number", type = "Integer" } | ||
{ name = "color", type = "String" } | ||
{ name = "weight", type = "Double" } | ||
{ name = "numberx2", type = "Integer" } | ||
{ name = "geom", type = "Point" } | ||
] | ||
} | ||
|
||
The following example will copy the attributes of the input features, while adding a new attribute (``numberx2``) derived | ||
from one of the input fields: | ||
|
||
:: | ||
|
||
geomesa.converters.myconverter = { | ||
type = "simple-feature" | ||
input-sft = "intype" | ||
fields = [ | ||
// note: number, color, weight, and geom will be auto-copied since they exist in both input and output types | ||
{ name = "numberx2", transform = "add($number, $number)::int" } | ||
] | ||
} | ||
|
||
.. tabs:: | ||
|
||
.. code-tab:: scala | ||
|
||
import org.geotools.api.feature.simple.SimpleFeature | ||
import org.locationtech.geomesa.convert.ConverterConfigLoader | ||
import org.locationtech.geomesa.convert2.simplefeature.FeatureToFeatureConverter | ||
import org.locationtech.geomesa.utils.collection.CloseableIterator | ||
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypeLoader | ||
|
||
val sft = SimpleFeatureTypeLoader.sftForName("outtype").getOrElse { | ||
throw new RuntimeException("Could not load feature type") | ||
} | ||
val conf = ConverterConfigLoader.configForName("myconverter").getOrElse { | ||
throw new RuntimeException("Could not load converter definition") | ||
} | ||
val converter = FeatureToFeatureConverter(sft, conf) | ||
try { | ||
val features: Iterator[SimpleFeature] = ??? // list of input features to transform | ||
val iter = converter.convert(CloseableIterator(features)) | ||
try { | ||
iter.foreach(???) // do something with the conversion result | ||
} finally { | ||
iter.close() | ||
} | ||
} finally { | ||
converter.close() // clean up any resources associated with your converter | ||
} | ||
|
||
.. code-tab:: java | ||
|
||
import com.typesafe.config.Config; | ||
import org.geotools.api.feature.simple.SimpleFeature; | ||
import org.geotools.api.feature.simple.SimpleFeatureType; | ||
import org.locationtech.geomesa.convert.ConverterConfigLoader; | ||
import org.locationtech.geomesa.convert.EvaluationContext; | ||
import org.locationtech.geomesa.convert2.simplefeature.FeatureToFeatureConverter; | ||
import org.locationtech.geomesa.utils.collection.CloseableIterator; | ||
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypeLoader; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
SimpleFeatureType outsft = SimpleFeatureTypeLoader.sftForName("outtype").get(); | ||
Config parserConf = ConverterConfigLoader.configForName("myconverter").get(); | ||
|
||
List<SimpleFeature> features = ...; // list of input features to transform | ||
|
||
// use try-with-resources to clean up the converter when we're done | ||
try (FeatureToFeatureConverter converter = FeatureToFeatureConverter.apply(outsft, parserConf)) { | ||
EvaluationContext context = converter.createEvaluationContext(Map.of()); | ||
try (CloseableIterator<SimpleFeature> iter = converter.convert(CloseableIterator.apply(features.iterator()), ec)) { | ||
while (iter.hasNext()) { | ||
iter.next(); // do something with the conversion result | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
...c/test/java/org/locationtech/geomesa/convert2/simplefeature/FeatureToFeatureJavaTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/*********************************************************************** | ||
* Copyright (c) 2013-2024 Commonwealth Computer Research, Inc. | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Apache License, Version 2.0 | ||
* which accompanies this distribution and is available at | ||
* http://www.opensource.org/licenses/apache2.0.php. | ||
***********************************************************************/ | ||
|
||
package org.locationtech.geomesa.convert2.simplefeature; | ||
|
||
import com.typesafe.config.Config; | ||
import com.typesafe.config.ConfigFactory; | ||
import org.geotools.api.feature.simple.SimpleFeature; | ||
import org.geotools.api.feature.simple.SimpleFeatureType; | ||
import org.geotools.feature.simple.SimpleFeatureBuilder; | ||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.locationtech.geomesa.convert.ConverterConfigLoader; | ||
import org.locationtech.geomesa.convert.EvaluationContext; | ||
import org.locationtech.geomesa.utils.collection.CloseableIterator; | ||
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypeLoader; | ||
import org.locationtech.geomesa.utils.interop.SimpleFeatureTypes; | ||
import org.locationtech.geomesa.utils.interop.WKTUtils; | ||
import org.locationtech.jts.geom.Geometry; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class FeatureToFeatureJavaTest { | ||
|
||
@Test | ||
public void testJavaApi() { | ||
Config outConfPoint = ConfigFactory.parseString( | ||
"{ type-name = outtype, attributes = [" + | ||
"{ name = number, type = Integer }," + | ||
"{ name = color, type = String }," + | ||
"{ name = weight, type = Double }," + | ||
"{ name = numberx2, type = Integer }," + | ||
"{ name = geom, type = Point }" + | ||
"]}"); | ||
|
||
Config parserConf = ConfigFactory.parseString( | ||
"{ type = simple-feature, input-sft = intype, fields = [" + | ||
"{ name = number, transform = \"$number\" }," + | ||
"{ name = color , transform = \"$color\" }," + | ||
"{ name = weight, transform = \"$weight\" }," + | ||
"{ name = geom, transform = \"$geom\" }," + | ||
"{ name = numberx2, transform = \"add($number, $number)::int\" }" + | ||
"]}"); | ||
|
||
SimpleFeatureType insft = SimpleFeatureTypeLoader.sftForName("intype").get(); | ||
SimpleFeatureType outsft = SimpleFeatureTypes.createType(outConfPoint); | ||
ConverterConfigLoader.configForName("myconverter"); | ||
FeatureToFeatureConverter converter = FeatureToFeatureConverter.apply(outsft, parserConf); | ||
Assert.assertNotNull(converter); | ||
|
||
Geometry pt = WKTUtils.read("POINT(0 0)"); | ||
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(insft); | ||
builder.reset(); | ||
builder.addAll(1, "blue", 10.0, pt); | ||
SimpleFeature sf = builder.buildFeature("1"); | ||
|
||
EvaluationContext ec = converter.createEvaluationContext(Map.of()); | ||
try(CloseableIterator<SimpleFeature> res = converter.convert(CloseableIterator.apply(List.of(sf).iterator()), ec)) { | ||
Assert.assertTrue(res.hasNext()); | ||
SimpleFeature next = res.next(); | ||
Assert.assertFalse(res.hasNext()); | ||
Assert.assertEquals(2, next.getAttribute("numberx2")); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |