[GIS] How to combine LineStrings using JTS

geotoolsjavajts-topology-suitelinestring

I want to combine LineStrings with common points. The scenario looks like this:
enter image description here

I tried it using the GeometryCollection.union() method like suggested here: http://docs.geotools.org/latest/userguide/library/jts/combine.html

I tried it using GeometryCollection and also MultiLineString objects but both seem not to work (same outputs). This is the Java method tooltip:
enter image description here

My code looks like this:

// the LineString-array source looks different but does not matter here!
LineString[] lineStringArray = this.getLineStringArray();
// create MultiLineString and GeometryCollection objects
MultiLineString multiLine = new GeometryFactory().createMultiLineString(lineStringArray);
GeometryCollection gc = new GeometryFactory().createGeometryCollection(lineStringArray);

// some info prints before union
System.out.println("+++++++ BEFORE UNION");
System.out.println("+++ MultiLineString...");
System.out.println("type of geometry: " + multiLine.getGeometryType());
System.out.println("num of geometries: " + multiLine.getNumGeometries());
System.out.println("WKT -> " + multiLine.toText());

System.out.println("+++ GeometryCollection...");
System.out.println("type of geometry: " + gc.getGeometryType());
System.out.println("num of geometries: " + gc.getNumGeometries());
System.out.println("WKT -> " + gc.toText());

// using the union method
Geometry multiLineUnion = multiLine.union();
Geometry gcUnion = gc.union();

// some info prints after union
System.out.println("+++++++ AFTER UNION");
System.out.println("+++ MultiLineString...");
System.out.println("type of geometry: " + multiLineUnion.getGeometryType());
System.out.println("num of geometries: " + multiLineUnion.getNumGeometries());
System.out.println("WKT -> " + multiLineUnion.toText());

System.out.println("+++ GeometryCollection...");
System.out.println("type of geometry: " + gcUnion.getGeometryType());
System.out.println("num of geometries: " + gcUnion.getNumGeometries());
System.out.println("WKT -> " + gcUnion.toText());

The output looks like this:

+++++++ BEFORE UNION
+++ MultiLineString...
type of geometry: MultiLineString
num of geometries: 11
WKT -> MULTILINESTRING ((254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938), (255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228), (255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264), (256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295), (256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444), (254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085), (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854), (256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939), (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354), (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712), (253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625))
+++ GeometryCollection...
type of geometry: GeometryCollection
num of geometries: 11
WKT -> GEOMETRYCOLLECTION (LINESTRING (254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938), LINESTRING (255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228), LINESTRING (255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264), LINESTRING (256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295), LINESTRING (256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444), LINESTRING (254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085), LINESTRING (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854), LINESTRING (256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939), LINESTRING (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354), LINESTRING (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712), LINESTRING (253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625))
+++++++ AFTER UNION
+++ MultiLineString...
type of geometry: MultiLineString
num of geometries: 11
WKT -> MULTILINESTRING ((254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938), (255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228), (255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264), (256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295), (256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444), (254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085), (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854), (256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939), (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354), (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712), (253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625))
+++ GeometryCollection...
type of geometry: MultiLineString
num of geometries: 11
WKT -> MULTILINESTRING ((254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938), (255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228), (255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264), (256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295), (256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444), (254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085), (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854), (256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939), (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354), (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712), (253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625))

This is the Maven dependency I am using:

<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-shapefile</artifactId>
    <version>14.3</version>
</dependency>

but I also tried this without a difference:

<dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts</artifactId>
    <version>1.12</version>
</dependency>

The expected outcome would of course be that there are just 4 geometries (combined LineStrings) after using the union() method. What am I doing wrong here?

EDIT:

Like iant pointed out the lines have to cross. I should have had a look at the implementation earlier to find out what "fully noding" means. Here the description from the implementation (https://sourceforge.net/p/jts-topo-suite/code/HEAD/tree/tags/Version_1.14/jts/java/src/com/vividsolutions/jts/operation/union/UnaryUnionOp.java):

Unioning a set of Linestrings has the effect of noding and dissolving
the input linework
. In this context "fully noded" means that there
will be an endpoint or node in the result for every endpoint or line
segment crossing in the input. "Dissolved" means that any duplicate
(i.e. coincident) line segments or portions of line segments will be
reduced to a single line segment in the result.

Best Answer

I think your problem is that none of your strings overlap so there is nothing for the union to do.

I've tried the following code:

SimpleFeatureIterator itr = features.features();
ArrayList<Geometry> geometries = new ArrayList<>();
try {
  while(itr.hasNext()) {
    SimpleFeature f = itr.next();
    Geometry geom = (Geometry)f.getDefaultGeometry();
    geometries.add(geom);
  }
  GeometryFactory factory = new GeometryFactory();

  GeometryCollection geometryCollection =
       (GeometryCollection) factory.buildGeometry( geometries );

  for(Geometry g:geometries)
    System.out.println(g);

  Geometry union = geometryCollection.union();
  System.out.println(union.getNumGeometries());
  System.out.println(union);
} finally {
  if(itr != null)
  itr.close();
}

Which (for a test data set) gives me the following:

MULTILINESTRING ((122883.2274738555 160974.9888089911, 210258.94921518277 200691.22596413989, 322599.1628826036 193126.22841077822, 393331.89000653516 114828.50373348492))
MULTILINESTRING ((130069.97514954908 94403.01033940841, 244301.4382053103 113315.5042228126, 303686.6689991994 22157.28370480449, 379336.6445328161 70951.51792398724))
MULTILINESTRING ((470873.1149284923 247972.4606726503, 324112.1623932759 129201.9990848721))
5
MULTILINESTRING ((122883.2274738555 160974.9888089911, 210258.94921518277 200691.22596413989, 322599.1628826036 193126.22841077822, 356597.513561129 155491.69049390784), 
(356597.513561129 155491.69049390784, 393331.89000653516 114828.50373348492), 
(130069.97514954908 94403.01033940841, 244301.4382053103 113315.5042228126, 303686.6689991994 22157.28370480449, 379336.6445328161 70951.51792398724), 
(470873.1149284923 247972.4606726503, 356597.513561129 155491.69049390784), 
(356597.513561129 155491.69049390784, 324112.1623932759 129201.9990848721))

or pictorially:

a map of the output

Edit

To join the lines together you need to use a LineMerger like so:

  String[] WKTS = { "LINESTRING (254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938)",
        "LINESTRING (255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228)",
        "LINESTRING (255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264)",
        "LINESTRING (256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295)",
        "LINESTRING (256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444)",
        "LINESTRING (254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085)",
        "LINESTRING (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854)",
        "LINESTRING (256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939)",
        "LINESTRING (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354)",
        "LINESTRING (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712)",
        "LINESTRING (253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625)" };
    WKTReader reader = new WKTReader();
    LineMerger merger = new LineMerger();
    for (String wkt : WKTS) {
      Geometry geom = reader.read(wkt);
      merger.add(geom);
    }

    Collection<LineString> collection = merger.getMergedLineStrings();

    for (LineString l : collection) {
      System.out.println(l);
    }

which gives the answer (I think) you want:

LINESTRING (252764.19811300343 475015.97340571403, 253526.90397945273 476194.3201198712, 254983.29259833007 475930.5996061625)
LINESTRING (254134.08645022905 471750.25133671844, 253091.20265363617 473772.75685744354)
LINESTRING (254058.76074485347 475001.2186020431, 255351.04293761664 474966.9279243938, 255529.29662365236 474272.4599921228, 256166.05830998957 473979.44920198264, 256082.8878282134 472762.2531920295, 254245.37250651795 472802.681197444, 254294.87550167093 473782.10435392085)
LINESTRING (255802.5570897992 475306.663459122, 256973.3861333156 473826.71649389854, 256996.781511873 472271.0759416939)

enter image description here

Related Question