diff --git a/.gitignore b/.gitignore
index e745b3e..c2fd4e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
# Resources in build output
bin/
+# Test output
+src/test/data/Results/
+
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/DxfWriter.java b/src/main/java/ch/geowerkstatt/lk2dxf/DxfWriter.java
index 9be787c..7c72fb5 100644
--- a/src/main/java/ch/geowerkstatt/lk2dxf/DxfWriter.java
+++ b/src/main/java/ch/geowerkstatt/lk2dxf/DxfWriter.java
@@ -12,16 +12,46 @@
import java.util.*;
import java.util.stream.Stream;
+/**
+ * Writes a DXF file from INTERLIS {@link IomObject}s.
+ * @see Autodesk DXF Reference
+ * @see ezdxf Documentation (additional info autout DXF format)
+ */
public final class DxfWriter implements AutoCloseable {
private final DecimalFormat decimalFormat;
private final Writer dxfWriter;
private int handle = 1;
- public DxfWriter(String filePath) throws IOException {
- this(filePath, 3, Collections.emptyList());
+ /**
+ * Creates a new DXF writer.
+ * @param writer The writer to write the DXF file to.
+ * @throws IOException If an error occurs while writing the DXF file.
+ */
+ public DxfWriter(Writer writer) throws IOException {
+ this(writer, 3, Collections.emptyList(), null);
}
- public DxfWriter(String filePath, int doublePrecision, Collection layerMappings) throws IOException {
+ /**
+ * Creates a new DXF writer.
+ * @param filePath The path to the DXF file to write.
+ * @param doublePrecision The number of decimal places to write for double values.
+ * @param layerMappings The layer mappings to use.
+ * @param comment The comment at the beginning of the DXF file. May be {@code null}.
+ * @throws IOException If an error occurs while writing the DXF file.
+ */
+ public DxfWriter(String filePath, int doublePrecision, Collection layerMappings, String comment) throws IOException {
+ this(new FileWriter(filePath, StandardCharsets.UTF_8), doublePrecision, layerMappings, comment);
+ }
+
+ /**
+ * Creates a new DXF writer.
+ * @param writer The writer to write the DXF file to.
+ * @param doublePrecision The number of decimal places to write for double values.
+ * @param layerMappings The layer mappings to use.
+ * @param comment The comment at the beginning of the DXF file. May be {@code null}.
+ * @throws IOException If an error occurs while writing the DXF file.
+ */
+ public DxfWriter(Writer writer, int doublePrecision, Collection layerMappings, String comment) throws IOException {
if (doublePrecision < 0) {
throw new IllegalArgumentException("doublePrecision must be positive or zero.");
}
@@ -29,14 +59,14 @@ public DxfWriter(String filePath, int doublePrecision, Collection
throw new IllegalArgumentException("layerMappings must not be null.");
}
- dxfWriter = new FileWriter(filePath, StandardCharsets.UTF_8);
+ dxfWriter = writer;
decimalFormat = new DecimalFormat("0." + "#".repeat(doublePrecision), new DecimalFormatSymbols(Locale.ROOT));
- prepareDxfForWritingEntities(layerMappings);
+ prepareDxfForWritingEntities(layerMappings, comment);
}
- private void prepareDxfForWritingEntities(Collection layerMappings) throws IOException {
- var layers = new TreeMap();
+ private void prepareDxfForWritingEntities(Collection layerMappings, String comment) throws IOException {
+ var layers = new LinkedHashMap(layerMappings.size());
layers.put("0", () -> writeLayer("0", "Continuous", 0));
for (var mapping : layerMappings) {
layers.putIfAbsent(mapping.layer(), () -> writeLayer(mapping.layer(), mapping.linetype(), mapping.color()));
@@ -52,6 +82,9 @@ private void prepareDxfForWritingEntities(Collection layerMappings
.map(s -> (ContentWriter) (() -> writeBlock(s, () -> writeCircle("0", 0, 0, 0.5)))))
.toArray(ContentWriter[]::new);
+ if (comment != null) {
+ writeElement(999, comment);
+ }
writeHeader();
writeSection("CLASSES");
@@ -66,7 +99,7 @@ private void prepareDxfForWritingEntities(Collection layerMappings
() -> writeLineType("DashDotDot", 0.5, -0.25, 0.0, -0.25, 0.0, -0.25)),
() -> writeTable("LAYER", layers.values().toArray(ContentWriter[]::new)),
() -> writeTable("STYLE",
- () -> writeStyle("cadastra", "cadastra_regular.ttf")),
+ layerMappings.stream().map(LayerMapping::font).map(f -> (ContentWriter) () -> writeStyle(f, f)).toArray(ContentWriter[]::new)),
() -> writeTable("VIEW"),
() -> writeTable("UCS"),
() -> writeTable("APPID",
@@ -86,6 +119,10 @@ private void finishDxfAfterWritingEntities() throws IOException {
writeElement(0, "EOF");
}
+ /**
+ * Writes a LWPOLYLINE (Leightweight POLYLINE) to the DXF file. This is a 2D polyline that supports arcs, the 3rd dimension is ignored if present.
+ * @see LWPOLYLINE (DXF Reference)
+ */
public void writeLwPolyline(String layerName, IomObject polyline) throws IOException {
var segments = polyline.getattrobj("sequence", 0);
var segmentCount = segments.getattrvaluecount("segment");
@@ -105,6 +142,10 @@ public void writeLwPolyline(String layerName, IomObject polyline) throws IOExcep
writePolylinePoints(polyline, isClosed);
}
+ /**
+ * Writes a HATCH to the DXF file. Arcs are supported, the 3rd dimension is ignored if present. The enclosed area is filled with a solid fill.
+ * @see HATCH (DXF Reference)
+ */
public void writeHatch(String layerName, IomObject multiSurface) throws IOException {
writeElement(0, "HATCH");
writeElement(5, getNextHandle());
@@ -189,6 +230,10 @@ private void writePolylinePoints(IomObject polyline, boolean isClosed) throws IO
}
}
+ /**
+ * Writes a CIRCLE to the DXF file.
+ * @see CIRCLE (DXF Reference)
+ */
public void writeCircle(String layerName, double centerX, double centerY, double radius) throws IOException {
writeElement(0, "CIRCLE");
writeElement(5, getNextHandle());
@@ -200,6 +245,10 @@ public void writeCircle(String layerName, double centerX, double centerY, double
writeElement(40, radius);
}
+ /**
+ * Writes a INSERT to the DXF file. The predefined block symbol {@code blockName} is inserted at the specified {@code point}.
+ * @see INSERT (DXF Reference)
+ */
public void writeBlockInsert(String layerName, String blockName, double rotation, IomObject point) throws IOException {
writeElement(0, "INSERT");
writeElement(5, getNextHandle());
@@ -209,25 +258,35 @@ public void writeBlockInsert(String layerName, String blockName, double rotation
writeElement(2, blockName);
writeElement(10, Double.parseDouble(point.getattrvalue("C1")));
writeElement(20, Double.parseDouble(point.getattrvalue("C2")));
- writeElement(50, rotation);
+ writeElement(50, convertOrientation(rotation));
}
+ /**
+ * Writes a TEXT to the DXF file.
+ * @param layerName The layer name.
+ * @param textStyle The name of the STYLE entry that defines the font.
+ * @param text The text string.
+ * @param hAlignment The horizontal alignment of the text. Values from the HALIGNMENT INTERLIS enumeration.
+ * @param vAlignment The vertical alignment of the text. Values from the VALIGNMENT INTERLIS enumeration.
+ * @param orientation The rotation angle of the text.
+ * @param position The position of the text.
+ * @see TEXT (DXF Reference)
+ */
public void writeText(String layerName, String textStyle, String text, String hAlignment, String vAlignment, double orientation, IomObject position) throws IOException {
- var hAlignmentValue = switch (hAlignment) {
- case "Right" -> 2;
- case "Center" -> 1;
+ var hAlignmentValue = switch (hAlignment.toLowerCase(Locale.ROOT)) {
+ case "right" -> 2;
+ case "center" -> 1;
default -> 0; // Left
};
- var vAlignmentValue = switch (vAlignment) {
- case "Top", "Cap" -> 3;
- case "Half" -> 2;
- case "Bottom" -> 1;
+ var vAlignmentValue = switch (vAlignment.toLowerCase(Locale.ROOT)) {
+ case "top", "cap" -> 3;
+ case "half" -> 2;
+ case "bottom" -> 1;
default -> 0; // Base
};
var isDefaultAlignment = hAlignmentValue == 0 && vAlignmentValue == 0;
- // Convert transfer orientation to DXF orientation
- orientation = (-orientation + 90 + 360) % 360;
+ orientation = convertOrientation(orientation);
writeElement(0, "TEXT");
writeElement(5, getNextHandle());
@@ -338,7 +397,7 @@ private void writeBlock(String name, ContentWriter... writeContent) throws IOExc
writeElement(100, "AcDbBlockEnd");
}
- public void writeMinimalDictionary() throws IOException {
+ private void writeMinimalDictionary() throws IOException {
var rootHandle = getNextHandle();
var entryHandle = getNextHandle();
@@ -424,6 +483,13 @@ private String getNextHandle() {
return Integer.toHexString(handle++).toUpperCase(Locale.ROOT);
}
+ /**
+ * Convert transfer orientation to DXF orientation.
+ */
+ private double convertOrientation(double angle) {
+ return (-angle + 90 + 360) % 360;
+ }
+
@Override
public void close() throws Exception {
finishDxfAfterWritingEntities();
diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/Main.java b/src/main/java/ch/geowerkstatt/lk2dxf/Main.java
index 2098d4d..af3074b 100644
--- a/src/main/java/ch/geowerkstatt/lk2dxf/Main.java
+++ b/src/main/java/ch/geowerkstatt/lk2dxf/Main.java
@@ -57,7 +57,7 @@ private static void processFiles(LK2DxfOptions options) {
Optional perimeter = options.parsePerimeter();
AtomicInteger counter = new AtomicInteger();
- try (var dxfWriter = new DxfWriter(options.dxfFile(), 3, ObjectMapper.getLayerMappings())) {
+ try (var dxfWriter = new DxfWriter(options.dxfFile(), 3, ObjectMapper.getLayerMappings(), "lk2dxf " + Main.VERSION)) {
for (String xtfFile : options.xtfFiles()) {
try (LKMapXtfReader reader = new LKMapXtfReader(new File(xtfFile))) {
ObjectMapper mapper = new ObjectMapper();
diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java b/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java
index f6cd862..2a721ab 100644
--- a/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java
+++ b/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java
@@ -29,6 +29,9 @@ public final class ObjectMapper {
}
}
+ /**
+ * Get the layer mappings asn a immutable list.
+ */
public static List getLayerMappings() {
return Collections.unmodifiableList(LAYER_MAPPINGS);
}
diff --git a/src/test/java/ch/geowerkstatt/lk2dxf/DxfWriterTest.java b/src/test/java/ch/geowerkstatt/lk2dxf/DxfWriterTest.java
new file mode 100644
index 0000000..2e776a7
--- /dev/null
+++ b/src/test/java/ch/geowerkstatt/lk2dxf/DxfWriterTest.java
@@ -0,0 +1,196 @@
+package ch.geowerkstatt.lk2dxf;
+
+import ch.geowerkstatt.lk2dxf.mapping.LayerMapping;
+import ch.interlis.iom.IomObject;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public final class DxfWriterTest {
+ private static final String TEST_OUT_DIR = "src/test/data/Results/DxfWriter/";
+
+ private StringWriter stringWriter;
+ private Writer testOutputWriter;
+
+ @BeforeAll
+ static void initAll() {
+ new File(TEST_OUT_DIR).mkdirs();
+ }
+
+ @BeforeEach
+ void init(TestInfo testInfo) throws IOException {
+ stringWriter = new StringWriter();
+ testOutputWriter = new MultiWriter(stringWriter, new FileWriter(TEST_OUT_DIR + testInfo.getDisplayName().replaceAll("\\W+", "") + ".dxf"));
+ }
+
+ @Test
+ public void writeEmptyDxf() throws Exception {
+ var dxfWriter = new DxfWriter(testOutputWriter);
+ dxfWriter.close();
+
+ assertEquals("0\nSECTION\n2\nHEADER\n9\n$ACADVER\n1\nAC1021\n9\n$HANDSEED\n5\n3b9aca00\n9\n$INSUNITS\n70\n6\n0\nENDSEC\n0\nSECTION\n2\nCLASSES\n0\nENDSEC\n0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nVPORT\n5\n1\n100\nAcDbSymbolTable\n70\n1\n0\nVPORT\n5\n2\n100\nAcDbSymbolTableRecord\n100\nAcDbViewportTableRecord\n2\n*ACTIVE\n70\n0\n0\nENDTAB\n0\nTABLE\n2\nLTYPE\n5\n3\n100\nAcDbSymbolTable\n70\n5\n0\nLTYPE\n5\n4\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n2\nByLayer\n70\n0\n72\n65\n73\n0\n40\n0\n0\nLTYPE\n5\n5\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n2\nByBlock\n70\n0\n72\n65\n73\n0\n40\n0\n0\nLTYPE\n5\n6\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n2\nContinuous\n70\n0\n72\n65\n73\n0\n40\n0\n0\nLTYPE\n5\n7\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n2\nDashed\n70\n0\n72\n65\n73\n2\n40\n0.75\n49\n0.5\n74\n0\n49\n-0.25\n74\n0\n0\nLTYPE\n5\n8\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n2\nDashDotDot\n70\n0\n72\n65\n73\n6\n40\n1.25\n49\n0.5\n74\n0\n49\n-0.25\n74\n0\n49\n0\n74\n0\n49\n-0.25\n74\n0\n49\n0\n74\n0\n49\n-0.25\n74\n0\n0\nENDTAB\n0\nTABLE\n2\nLAYER\n5\n9\n100\nAcDbSymbolTable\n70\n1\n0\nLAYER\n5\nA\n100\nAcDbSymbolTableRecord\n100\nAcDbLayerTableRecord\n2\n0\n6\nContinuous\n370\n25\n62\n0\n70\n0\n390\n0\n0\nENDTAB\n0\nTABLE\n2\nSTYLE\n5\nB\n100\nAcDbSymbolTable\n70\n0\n0\nENDTAB\n0\nTABLE\n2\nVIEW\n5\nC\n100\nAcDbSymbolTable\n70\n0\n0\nENDTAB\n0\nTABLE\n2\nUCS\n5\nD\n100\nAcDbSymbolTable\n70\n0\n0\nENDTAB\n0\nTABLE\n2\nAPPID\n5\nE\n100\nAcDbSymbolTable\n70\n1\n0\nAPPID\n5\nF\n100\nAcDbSymbolTableRecord\n100\nAcDbRegAppTableRecord\n2\nACAD\n70\n0\n0\nENDTAB\n0\nTABLE\n2\nDIMSTYLE\n5\n10\n100\nAcDbSymbolTable\n100\nAcDbDimStyleTable\n70\n0\n71\n1\n0\nENDTAB\n0\nTABLE\n2\nBLOCK_RECORD\n5\n11\n100\nAcDbSymbolTable\n70\n2\n0\nBLOCK_RECORD\n5\n12\n100\nAcDbSymbolTableRecord\n100\nAcDbBlockTableRecord\n2\n*Model_Space\n70\n0\n280\n1\n281\n0\n0\nBLOCK_RECORD\n5\n13\n100\nAcDbSymbolTableRecord\n100\nAcDbBlockTableRecord\n2\n*Paper_Space\n70\n0\n280\n1\n281\n0\n0\nENDTAB\n0\nENDSEC\n0\nSECTION\n2\nBLOCKS\n0\nBLOCK\n5\n14\n8\n0\n100\nAcDbEntity\n100\nAcDbBlockBegin\n2\n*Model_Space\n70\n0\n10\n0\n20\n0\n30\n0\n0\nENDBLK\n5\n15\n8\n0\n100\nAcDbEntity\n100\nAcDbBlockEnd\n0\nBLOCK\n5\n16\n8\n0\n100\nAcDbEntity\n100\nAcDbBlockBegin\n2\n*Paper_Space\n70\n0\n10\n0\n20\n0\n30\n0\n0\nENDBLK\n5\n17\n8\n0\n100\nAcDbEntity\n100\nAcDbBlockEnd\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n0\nENDSEC\n0\nSECTION\n2\nOBJECTS\n0\nDICTIONARY\n5\n18\n330\n0\n100\nAcDbDictionary\n281\n1\n3\nACAD_GROUP\n350\n19\n0\nDICTIONARY\n5\n19\n330\n18\n100\nAcDbDictionary\n281\n1\n0\nENDSEC\n0\nEOF\n", stringWriter.toString());
+ }
+
+ @Test
+ public void writeCircle() throws Exception {
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeCircle("LAYER_NAME", 1, 2, 3);
+ assertEquals("0\nCIRCLE\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbCircle\n10\n1\n20\n2\n40\n3\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeOpenPolyline() throws Exception {
+ var polyline = IomObjectHelper.createPolyline(
+ IomObjectHelper.createCoord("1", "2"),
+ IomObjectHelper.createCoord("3", "4"),
+ IomObjectHelper.createCoord("5", "4.5"));
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeLwPolyline("LAYER_NAME", polyline);
+ assertEquals("0\nLWPOLYLINE\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbPolyline\n90\n3\n70\n0\n10\n1\n20\n2\n42\n0\n10\n3\n20\n4\n42\n0\n10\n5\n20\n4.5\n42\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeClosedPolyline() throws Exception {
+ var polyline = IomObjectHelper.createPolyline(
+ IomObjectHelper.createCoord("10", "50"),
+ IomObjectHelper.createCoord("20", "70"),
+ IomObjectHelper.createCoord("20", "50"),
+ IomObjectHelper.createCoord("10", "50"));
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeLwPolyline("LAYER_NAME", polyline);
+ assertEquals("0\nLWPOLYLINE\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbPolyline\n90\n3\n70\n1\n10\n10\n20\n50\n42\n0\n10\n20\n20\n70\n42\n0\n10\n20\n20\n50\n42\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writePolylineWithArcs() throws Exception {
+ var polylineCcwArcs = IomObjectHelper.createPolyline(createArcTestSegments(10, 12, 5));
+ var polylineCwArcs = IomObjectHelper.createPolyline(createArcTestSegments(9, 6, 5));
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeLwPolyline("CCW-Arcs", polylineCcwArcs);
+ assertEquals("0\nLWPOLYLINE\n5\n18\n100\nAcDbEntity\n8\nCCW-Arcs\n100\nAcDbPolyline\n90\n6\n70\n0\n10\n10\n20\n0\n42\n0.665\n10\n3.09\n20\n9.511\n42\n0.665\n10\n-8.09\n20\n5.878\n42\n0.665\n10\n-8.09\n20\n-5.878\n42\n0.665\n10\n3.09\n20\n-9.511\n42\n0.665\n10\n10\n20\n-0\n42\n0\n", stringWriter.toString());
+
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeLwPolyline("CW-Arcs", polylineCwArcs);
+ assertEquals("0\nLWPOLYLINE\n5\n19\n100\nAcDbEntity\n8\nCW-Arcs\n100\nAcDbPolyline\n90\n6\n70\n0\n10\n9\n20\n0\n42\n-0.242\n10\n2.781\n20\n8.56\n42\n-0.242\n10\n-7.281\n20\n5.29\n42\n-0.242\n10\n-7.281\n20\n-5.29\n42\n-0.242\n10\n2.781\n20\n-8.56\n42\n-0.242\n10\n9\n20\n-0\n42\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeHatch() throws Exception {
+ var surface = IomObjectHelper.createRectangleGeometry("10", "20", "50", "70");
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeHatch("LAYER_NAME", surface);
+ assertEquals("0\nHATCH\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbHatch\n10\n0\n20\n0\n30\n0\n210\n0\n220\n0\n230\n1\n2\nSOLID\n70\n1\n71\n0\n91\n1\n92\n3\n72\n1\n73\n1\n93\n4\n10\n10\n20\n20\n42\n0\n10\n10\n20\n70\n42\n0\n10\n50\n20\n70\n42\n0\n10\n50\n20\n20\n42\n0\n97\n0\n75\n0\n76\n1\n98\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeHatchWithHoles() throws Exception {
+ var surface = IomObjectHelper.createPolygonFromBoundaries(
+ IomObjectHelper.createRectangleBoundary("10", "50", "20", "70"),
+ IomObjectHelper.createRectangleBoundary("11", "51", "19", "59.5"),
+ IomObjectHelper.createRectangleBoundary("11", "60.5", "19", "69"));
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeHatch("LAYER_NAME", surface);
+ assertEquals("0\nHATCH\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbHatch\n10\n0\n20\n0\n30\n0\n210\n0\n220\n0\n230\n1\n2\nSOLID\n70\n1\n71\n0\n91\n3\n92\n3\n72\n1\n73\n1\n93\n4\n10\n10\n20\n50\n42\n0\n10\n10\n20\n70\n42\n0\n10\n20\n20\n70\n42\n0\n10\n20\n20\n50\n42\n0\n97\n0\n92\n18\n72\n1\n73\n1\n93\n4\n10\n11\n20\n51\n42\n0\n10\n11\n20\n59.5\n42\n0\n10\n19\n20\n59.5\n42\n0\n10\n19\n20\n51\n42\n0\n97\n0\n92\n18\n72\n1\n73\n1\n93\n4\n10\n11\n20\n60.5\n42\n0\n10\n11\n20\n69\n42\n0\n10\n19\n20\n69\n42\n0\n10\n19\n20\n60.5\n42\n0\n97\n0\n75\n0\n76\n1\n98\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeHatchWithArcs() throws Exception {
+ var surface = IomObjectHelper.createPolygonFromBoundaries(
+ IomObjectHelper.createBoundary(createArcTestSegments(20, 25, 6)));
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeHatch("LAYER_NAME", surface);
+ assertEquals("0\nHATCH\n5\n18\n100\nAcDbEntity\n8\nLAYER_NAME\n100\nAcDbHatch\n10\n0\n20\n0\n30\n0\n210\n0\n220\n0\n230\n1\n2\nSOLID\n70\n1\n71\n0\n91\n1\n92\n3\n72\n1\n73\n1\n93\n6\n10\n20\n20\n0\n42\n0.768\n10\n10\n20\n17.321\n42\n0.768\n10\n-10\n20\n17.321\n42\n0.768\n10\n-20\n20\n0\n42\n0.768\n10\n-10\n20\n-17.321\n42\n0.768\n10\n10\n20\n-17.321\n42\n0.768\n97\n0\n75\n0\n76\n1\n98\n0\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeBlockInsert() throws Exception {
+ var point = IomObjectHelper.createCoord("10", "20");
+
+ try (var dxfWriter = new DxfWriter(testOutputWriter, 3, createTestLayerMappings(), null)) {
+ stringWriter.getBuffer().setLength(0);
+ dxfWriter.writeBlockInsert("Test", "TestSymbol", 30, point);
+ assertEquals("0\nINSERT\n5\n1E\n100\nAcDbEntity\n8\nTest\n100\nAcDbBlockReference\n2\nTestSymbol\n10\n10\n20\n20\n50\n60\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeTextAlignment() throws Exception {
+ try (var dxfWriter = new DxfWriter(testOutputWriter, 3, createTestLayerMappings(), null)) {
+ stringWriter.getBuffer().setLength(0);
+
+ int y = 0;
+ for (var vAlign : List.of("Top", "Cap", "Half", "Base", "Bottom")) {
+ for (var hAlign : List.of("Left", "Center", "Right")) {
+ dxfWriter.writeText("Test", "arial", vAlign + "-" + hAlign, hAlign, vAlign, 90, IomObjectHelper.createCoord("0", Integer.toString(y)));
+ dxfWriter.writeCircle("Anchor", 0, y, 0.5);
+ y += 4;
+ }
+ }
+
+ assertEquals("0\nTEXT\n5\n1E\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nTop-Left\n11\n0\n21\n0\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n1F\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n0\n40\n0.5\n0\nTEXT\n5\n20\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nTop-Center\n72\n1\n11\n0\n21\n4\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n21\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n4\n40\n0.5\n0\nTEXT\n5\n22\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nTop-Right\n72\n2\n11\n0\n21\n8\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n23\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n8\n40\n0.5\n0\nTEXT\n5\n24\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nCap-Left\n11\n0\n21\n12\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n25\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n12\n40\n0.5\n0\nTEXT\n5\n26\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nCap-Center\n72\n1\n11\n0\n21\n16\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n27\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n16\n40\n0.5\n0\nTEXT\n5\n28\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nCap-Right\n72\n2\n11\n0\n21\n20\n100\nAcDbText\n73\n3\n0\nCIRCLE\n5\n29\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n20\n40\n0.5\n0\nTEXT\n5\n2A\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nHalf-Left\n11\n0\n21\n24\n100\nAcDbText\n73\n2\n0\nCIRCLE\n5\n2B\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n24\n40\n0.5\n0\nTEXT\n5\n2C\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nHalf-Center\n72\n1\n11\n0\n21\n28\n100\nAcDbText\n73\n2\n0\nCIRCLE\n5\n2D\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n28\n40\n0.5\n0\nTEXT\n5\n2E\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nHalf-Right\n72\n2\n11\n0\n21\n32\n100\nAcDbText\n73\n2\n0\nCIRCLE\n5\n2F\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n32\n40\n0.5\n0\nTEXT\n5\n30\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n36\n40\n1.25\n1\nBase-Left\n100\nAcDbText\n0\nCIRCLE\n5\n31\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n36\n40\n0.5\n0\nTEXT\n5\n32\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nBase-Center\n72\n1\n11\n0\n21\n40\n100\nAcDbText\n0\nCIRCLE\n5\n33\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n40\n40\n0.5\n0\nTEXT\n5\n34\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nBase-Right\n72\n2\n11\n0\n21\n44\n100\nAcDbText\n0\nCIRCLE\n5\n35\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n44\n40\n0.5\n0\nTEXT\n5\n36\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nBottom-Left\n11\n0\n21\n48\n100\nAcDbText\n73\n1\n0\nCIRCLE\n5\n37\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n48\n40\n0.5\n0\nTEXT\n5\n38\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nBottom-Center\n72\n1\n11\n0\n21\n52\n100\nAcDbText\n73\n1\n0\nCIRCLE\n5\n39\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n52\n40\n0.5\n0\nTEXT\n5\n3A\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nBottom-Right\n72\n2\n11\n0\n21\n56\n100\nAcDbText\n73\n1\n0\nCIRCLE\n5\n3B\n100\nAcDbEntity\n8\nAnchor\n100\nAcDbCircle\n10\n0\n20\n56\n40\n0.5\n", stringWriter.toString());
+ }
+ }
+
+ @Test
+ public void writeTextOrientation() throws Exception {
+ try (var dxfWriter = new DxfWriter(testOutputWriter, 3, createTestLayerMappings(), null)) {
+ stringWriter.getBuffer().setLength(0);
+
+ for (int orientation = 0; orientation < 360; orientation += 45) {
+ dxfWriter.writeText("Test", "arial", "Orientation: " + orientation + "°", "Left", "Base", orientation, IomObjectHelper.createCoord("0", "0"));
+ }
+ assertEquals("0\nTEXT\n5\n1E\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 0°\n50\n90\n100\nAcDbText\n0\nTEXT\n5\n1F\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 45°\n50\n45\n100\nAcDbText\n0\nTEXT\n5\n20\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 90°\n100\nAcDbText\n0\nTEXT\n5\n21\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 135°\n50\n315\n100\nAcDbText\n0\nTEXT\n5\n22\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 180°\n50\n270\n100\nAcDbText\n0\nTEXT\n5\n23\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 225°\n50\n225\n100\nAcDbText\n0\nTEXT\n5\n24\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 270°\n50\n180\n100\nAcDbText\n0\nTEXT\n5\n25\n100\nAcDbEntity\n8\nTest\n100\nAcDbText\n7\narial\n10\n0\n20\n0\n40\n1.25\n1\nOrientation: 315°\n50\n135\n100\nAcDbText\n", stringWriter.toString());
+ }
+ }
+
+ private IomObject[] createArcTestSegments(double pointRadius, double midPointRadius, int segmentCount) {
+ var segments = new IomObject[segmentCount + 1];
+ segments[0] = IomObjectHelper.createCoord(Double.toString(pointRadius), "0");
+ for (int i = 0; i < segmentCount; i++) {
+ var pointAngle = Math.TAU * ((i + 1.0) / segmentCount);
+ var midAngle = Math.TAU * ((i + 0.5) / segmentCount);
+ segments[i + 1] = IomObjectHelper.createArc(
+ Double.toString(midPointRadius * Math.cos(midAngle)),
+ Double.toString(midPointRadius * Math.sin(midAngle)),
+ Double.toString(pointRadius * Math.cos(pointAngle)),
+ Double.toString(pointRadius * Math.sin(pointAngle)));
+ }
+
+ return segments;
+ }
+
+ private Collection createTestLayerMappings() {
+ return List.of(new LayerMapping("Test", "", "", "", 1, "", "", "", "", "TestSymbol", "", 0.25, 1.25, "arial", null));
+ }
+}
diff --git a/src/test/java/ch/geowerkstatt/lk2dxf/IomObjectHelper.java b/src/test/java/ch/geowerkstatt/lk2dxf/IomObjectHelper.java
index 34a0230..c3ecd21 100644
--- a/src/test/java/ch/geowerkstatt/lk2dxf/IomObjectHelper.java
+++ b/src/test/java/ch/geowerkstatt/lk2dxf/IomObjectHelper.java
@@ -3,7 +3,9 @@
import ch.interlis.iom.IomObject;
import ch.interlis.iom_j.Iom_jObject;
-public class IomObjectHelper {
+public final class IomObjectHelper {
+ private IomObjectHelper() {
+ }
/**
* Create an IomObject containing a rectangle surface geometry with the specified corner coordinates.
diff --git a/src/test/java/ch/geowerkstatt/lk2dxf/LKMapXtfReaderTest.java b/src/test/java/ch/geowerkstatt/lk2dxf/LKMapXtfReaderTest.java
index c18c29b..7df1b93 100644
--- a/src/test/java/ch/geowerkstatt/lk2dxf/LKMapXtfReaderTest.java
+++ b/src/test/java/ch/geowerkstatt/lk2dxf/LKMapXtfReaderTest.java
@@ -1,12 +1,10 @@
package ch.geowerkstatt.lk2dxf;
-import ch.geowerkstatt.lk2dxf.mapping.ObjectMapper;
import ch.interlis.iom.IomObject;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.stream.Stream;
-import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -53,30 +51,4 @@ public void multipleReadsNotAllowed() throws Exception {
assertThrows(IllegalStateException.class, reader::readObjects, "Multiple calls to readObjects should throw an exception");
}
}
-
- @Test
- public void readXtfForKulm() throws Exception {
- final var dxfWriter = new DxfWriter(TEST_DIR + "partial_bad.dxf", 3, ObjectMapper.getLayerMappings());
- try(dxfWriter) {
- var polyline = IomObjectHelper.createPolyline(
- IomObjectHelper.createCoord("0", "0"),
- IomObjectHelper.createCoord("0", "200"),
- IomObjectHelper.createArc("120", "120", "200", "0"),
- IomObjectHelper.createCoord("100", "0"));
-
- var surface = IomObjectHelper.createPolygonFromBoundaries(
- IomObjectHelper.createBoundary(
- IomObjectHelper.createCoord("10", "10"),
- IomObjectHelper.createCoord("10", "190"),
- IomObjectHelper.createArc("70", "70", "190", "10"),
- IomObjectHelper.createCoord("10", "10")),
- //IomObjectHelper.createRectangleBoundary("0", "0", "60", "60"),
- IomObjectHelper.createRectangleBoundary("20", "20", "60", "60"));
-
- var point = IomObjectHelper.createCoord("120", "120");
-
- dxfWriter.writeLwPolyline("ELE-LINIE-UNGENAU", polyline);
- dxfWriter.writeBlockInsert("ELE-PUNKT", "BEW15", 0, point);
- }
- }
}
diff --git a/src/test/java/ch/geowerkstatt/lk2dxf/MultiWriter.java b/src/test/java/ch/geowerkstatt/lk2dxf/MultiWriter.java
new file mode 100644
index 0000000..1a1b6e0
--- /dev/null
+++ b/src/test/java/ch/geowerkstatt/lk2dxf/MultiWriter.java
@@ -0,0 +1,33 @@
+package ch.geowerkstatt.lk2dxf;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public final class MultiWriter extends Writer {
+ private final Writer[] writers;
+
+ public MultiWriter(Writer... writers) {
+ this.writers = writers;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ for (Writer writer : writers) {
+ writer.write(cbuf, off, len);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ for (Writer writer : writers) {
+ writer.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ for (Writer writer : writers) {
+ writer.close();
+ }
+ }
+}