mreutegg / laszip4j

The LASzip library ported to Java
GNU Lesser General Public License v2.1
34 stars 15 forks source link

Trouble extracting SwissTopo data #46

Closed pablo-mayrgundter closed 2 years ago

pablo-mayrgundter commented 2 years ago

I'm happy to have found your tool, and am able to get a simple test program running to convert LAS->XYZ, but not able to use the jar on a las directly. The datafiles from SwissTopo are too big too work with in XYZ, so I'd like to try getting the Jar program working instead. Hoping you can help since you seem to have been looking at the same data :)

I've built the code (on OSX, java 17) with:

mvn package

which yields: laszip4j-0.9-SNAPSHOT.jar

And downloaded my target from SwissTopo, and extracted the single .las file:

544M    swisssurface3d_2019_2687-1169_2056_5728.las.zip
1.1G    2687_1169.las

Running:

java -jar target/laszip4j-0.9-SNAPSHOT.jar -oparse xyzc -i 2687_1169.las  -keep_class 3 4 5 6 10 -o out

I get:

pablo@top:~/laszip4j> java -jar target/laszip4j-0.9-SNAPSHOT.jar -oparse xyzc -i 2687_1169.las  -keep_class 3 4 5 6 10 -o out
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin -1, end 3, length 3
        at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4601)
        at java.base/java.lang.String.substring(String.java:2704)
        at java.base/java.lang.String.substring(String.java:2677)
        at com.github.mreutegg.laszip4j.laslib.LASwriteOpener.set_file_name(LASwriteOpener.java:439)
        at com.github.mreutegg.laszip4j.laslib.LASwriteOpener.parse(LASwriteOpener.java:254)
        at com.github.mreutegg.laszip4j.lastools.Laszip.run(Laszip.java:91)
        at com.github.mreutegg.laszip4j.lastools.Laszip.main(Laszip.java:46)

btw, here's my LAS->XYZ program:

import java.io.File;
import com.github.mreutegg.laszip4j.LASReader;
import com.github.mreutegg.laszip4j.LASPoint;

class Test {
  public static void main(String [] args) throws Exception {
    LASReader reader = new LASReader(new File(args[0]));
    for (LASPoint p : reader.getPoints()) {
      // read something from point
      //p.getClassification();
      System.out.printf("%d %d %d\n", p.getX(), p.getY(), p.getZ());
    }
  }
}
mreutegg commented 2 years ago

Parsing the filename of the output file seems to be broken when the name is less than 4 characters. Can you try with a slightly longer name? E.g. -o out.xyz

mreutegg commented 2 years ago

Btw, you don’t necessarily have to build the jar file. Releases are published to a public maven repository. E.g. here https://repo1.maven.org/maven2/com/github/mreutegg/laszip4j/0.8/laszip4j-0.8.jar

pablo-mayrgundter commented 2 years ago

Ok, that did the trick, thanks!

Another question now that I'm working with the data. Should I be able to extract general XYZ data like the topography from SwissTopo? I'm currently getting numbers not in the correct range according to their page, so maybe I'm using the wrong API.

I downloaded the LAS from here:

https://www.swisstopo.admin.ch/en/geodata/height/surface3d.html

It's supposed to be LV95 coordinate format, a substitute to their old XYZ format. LV95 coords are (2600000+,1100000+), but the values I'm getting from laszip are (99999-,99999-)

Here's my script:

> java -cp target/laszip4j-0.9-SNAPSHOT.jar:. Test 2687_1169.las 2687_1169.xyz
File bounding box is from min:({0.000000},{0.000000}) to max:({99999.000000},{99999.000000})
> du -sh 2687_1169.*
1.1G    2687_1169.las
1.6G    2687_1169.xyz
> head -3 2687_1169.xyz
99936.0 9708.0 109255.0
99686.0 9782.0 109344.0
99675.0 9808.0 109298.0

I expected output should look something like this:

2687000.5 1169000.0 100.5
...

Here's the extended program to capture min/max:

import java.io.*;
import com.github.mreutegg.laszip4j.LASReader;
import com.github.mreutegg.laszip4j.LASPoint;

class Test {
  public static void main(String [] args) throws Exception {
    float
      minX = Float.MAX_VALUE, minY = Float.MAX_VALUE,
      maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE;
    LASReader reader = new LASReader(new File(args[0]));
    PrintWriter w = new PrintWriter(new FileWriter(new File(args[1])));
    for (LASPoint p : reader.getPoints()) {
      // read something from point
      // p.getClassification();
      float x = p.getX();
      float y = p.getY();
      float z = p.getZ();
      if (x <= minX) {
        minX = x;
      }
      if (y <= minY) {
        minY = y;
      }
      if (x >= maxX) {
        maxX = x;
      }
      if (y >= maxY) {
        maxY = y;
      }
      w.printf("%.1f %.1f %.1f\n", x, y, z);
    }
    w.flush();
    System.out.printf(
        "File bounding box is from min:({%f},{%f}) to max:({%f},{%f})\n",
        minX, minY, maxX, maxY);
  }
}
mreutegg commented 2 years ago

It's good to hear that it works with the longer file name.

At least the output using the runnable jar file looks reasonable to me. I picked a random .las from swissSURFACE3D and converted it with the command line you mentioned earlier. Then I get:

$ head -3 2702_1239.xyz 
2702723.95 1239713.88 562.71 3
2702175.84 1239784.76 530.40 3
2702175.82 1239785.10 530.24 3

Keep in mind that your test program reads data as defined by the LAS specification. That is, you cannot just use the X, Y and Z values as is. The LAS specification also defines an offset and a scale for each of those value in the header. What do you get when you take those into account? The header data can be accessed via LASReader.getHeader().

pablo-mayrgundter commented 2 years ago

Awesome! That did it, thanks :)

> java -cp target/laszip4j-0.9-SNAPSHOT.jar:.  Test 2687_1169.las 2687_1169.xyz
scale: x:0.010000 y:0.010000 z:0.010000, offset: x:2687000.000000, y:1169000.000000 z:0.000000
File bounding box is from min:({2687000.000000},{1169000.000000}) to max:({2687999.990000},{1169999.990000})
pablo@top:~/laszip4j> head 2687_1169.xyz
2687999.4 1169097.1 1092.6
2687996.9 1169097.8 1093.4
2687996.8 1169098.1 1093.0
class Test {
  public static void main(String [] args) throws Exception {
    double
      minX = Double.MAX_VALUE, minY = Double.MAX_VALUE,
      maxX = Double.MIN_VALUE, maxY = Double.MIN_VALUE;
    LASReader reader = new LASReader(new File(args[0]));
    PrintWriter w = new PrintWriter(new FileWriter(new File(args[1])));
    LASHeader header = reader.getHeader();
    double
      xScale = header.getXScaleFactor(),
      yScale = header.getYScaleFactor(),
      zScale = header.getZScaleFactor(),
      xOff = header.getXOffset(),
      yOff = header.getYOffset(),
      zOff = header.getZOffset();
    System.out.printf("scale: x:%f y:%f z:%f, offset: x:%f, y:%f z:%f\n",
                      xScale, yScale, zScale, xOff, yOff, zOff);
    for (LASPoint p : reader.getPoints()) {
      // read something from point
      // p.getClassification();
      double x = xOff + xScale * p.getX();
      double y = yOff + yScale * p.getY();
      double z = zOff + zScale * p.getZ();
      if (x <= minX) {
        minX = x;
      }
      if (y <= minY) {
        minY = y;
      }
      if (x >= maxX) {
        maxX = x;
      }
      if (y >= maxY) {
        maxY = y;
      }
      w.printf("%.1f %.1f %.1f\n", x, y, z);
    }
    w.flush();
    System.out.printf(
        "File bounding box is from min:({%f},{%f}) to max:({%f},{%f})\n",
        minX, minY, maxX, maxY);
  }
}