From fb7a2105013ee821873f684caab93826cecaf93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Rulka?= Date: Fri, 6 Feb 2026 20:16:40 +0100 Subject: [PATCH] =?UTF-8?q?Wersja=20dzia=C5=82aj=C4=85ca.=20Release.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logback.xml | 4 +- pom.xml | 2 +- src/main/java/pl/wat/ms4ds/terrain/AStar.java | 4 +- .../java/pl/wat/ms4ds/terrain/Bresenham.java | 150 -- src/main/java/pl/wat/ms4ds/terrain/Coord.java | 69 +- .../pl/wat/ms4ds/terrain/CoordConversion.java | 158 --- .../java/pl/wat/ms4ds/terrain/GeoCoord.java | 19 - .../java/pl/wat/ms4ds/terrain/GeomUtils.java | 1262 ++++++----------- .../pl/wat/ms4ds/terrain/RightBigSquare.java | 45 +- .../java/pl/wat/ms4ds/terrain/Square.java | 152 +- src/main/java/pl/wat/ms4ds/terrain/Teren.java | 72 +- .../pl/wat/ms4ds/terrain/TerrainType.java | 26 +- .../pl/wat/ms4ds/terrain/TerrainUtils.java | 94 +- .../nmt/{NMTData.java => NmtData.java} | 4 +- ...TDataReader.java => NmtDataGenerator.java} | 133 +- ...DataProvider.java => NmtDataProvider.java} | 11 +- .../terrain/{konwersja => osm}/CoordTest.java | 6 +- .../{konwersja => osm}/CoordUtils.java | 14 +- .../{konwersja => osm}/EAreaFeature.java | 2 +- .../{konwersja => osm}/ELinearFeature.java | 2 +- .../{konwersja => osm}/EOSMAmenity.java | 2 +- .../{konwersja => osm}/EOSMBridge.java | 2 +- .../{konwersja => osm}/EOSMBuilding.java | 2 +- .../{konwersja => osm}/EOSMHighway.java | 2 +- .../{konwersja => osm}/EOSMLandcover.java | 2 +- .../{konwersja => osm}/EOSMLanduse.java | 2 +- .../{konwersja => osm}/EOSMNatural.java | 2 +- .../terrain/{konwersja => osm}/EOSMWater.java | 2 +- .../{konwersja => osm}/EOSMWaterway.java | 2 +- .../{konwersja => osm}/EsriFileReader.java | 18 +- .../terrain/{konwersja => osm}/MapBounds.java | 2 +- .../terrain/{konwersja => osm}/Node.java | 8 +- .../OpenStreetMapReader.java | 6 +- .../terrain/{konwersja => osm}/Relation.java | 2 +- .../ms4ds/terrain/{konwersja => osm}/Way.java | 175 ++- .../terrain/{konwersja => osm}/Worker.java | 12 +- .../terrain/osm/shapefile/DbfRecord.java | 66 - .../wat/ms4ds/terrain/osm/shapefile/Main.java | 23 - .../{osm/shapefile => osmshp}/DbfField.java | 2 +- .../{osm/shapefile => osmshp}/DbfHeader.java | 2 +- .../wat/ms4ds/terrain/osmshp/DbfRecord.java | 130 ++ .../OsmShapeFileReader.java | 49 +- .../terrain/osmshp/OsmShpDataGenerator.java | 581 ++++++++ .../{osm/shapefile => osmshp}/ShpHeader.java | 32 +- .../shapefile => osmshp}/ShpMultiPoint.java | 8 +- .../{osm/shapefile => osmshp}/ShpPoint.java | 8 +- .../shapefile => osmshp}/ShpPolyLine.java | 4 +- .../ShpPolyShape.java} | 21 +- .../{osm/shapefile => osmshp}/ShpPolygon.java | 4 +- .../{osm/shapefile => osmshp}/ShpShape.java | 53 +- teren.properties | 4 +- 51 files changed, 1674 insertions(+), 1783 deletions(-) delete mode 100644 src/main/java/pl/wat/ms4ds/terrain/Bresenham.java delete mode 100644 src/main/java/pl/wat/ms4ds/terrain/CoordConversion.java delete mode 100644 src/main/java/pl/wat/ms4ds/terrain/GeoCoord.java rename src/main/java/pl/wat/ms4ds/terrain/nmt/{NMTData.java => NmtData.java} (95%) rename src/main/java/pl/wat/ms4ds/terrain/nmt/{NMTDataReader.java => NmtDataGenerator.java} (93%) rename src/main/java/pl/wat/ms4ds/terrain/nmt/{NMTDataProvider.java => NmtDataProvider.java} (96%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/CoordTest.java (98%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/CoordUtils.java (91%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EAreaFeature.java (66%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/ELinearFeature.java (62%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMAmenity.java (97%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMBridge.java (88%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMBuilding.java (94%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMHighway.java (97%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMLandcover.java (90%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMLanduse.java (91%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMNatural.java (91%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMWater.java (93%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EOSMWaterway.java (93%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/EsriFileReader.java (94%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/MapBounds.java (74%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/Node.java (91%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/OpenStreetMapReader.java (99%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/Relation.java (99%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/Way.java (79%) rename src/main/java/pl/wat/ms4ds/terrain/{konwersja => osm}/Worker.java (66%) delete mode 100644 src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfRecord.java delete mode 100644 src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/Main.java rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/DbfField.java (94%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/DbfHeader.java (98%) create mode 100644 src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfRecord.java rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/OsmShapeFileReader.java (75%) create mode 100644 src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShpDataGenerator.java rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpHeader.java (80%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpMultiPoint.java (91%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpPoint.java (81%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpPolyLine.java (83%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile/ShpPollyShape.java => osmshp/ShpPolyShape.java} (80%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpPolygon.java (83%) rename src/main/java/pl/wat/ms4ds/terrain/{osm/shapefile => osmshp}/ShpShape.java (77%) diff --git a/logback.xml b/logback.xml index dac4579..5b4e8f6 100644 --- a/logback.xml +++ b/logback.xml @@ -17,11 +17,11 @@ - + - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7e98c36..f7fa0b3 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ true - pl.wat.ms4ds.terrain.nmt.NMTDataProvider + pl.wat.ms4ds.terrain.nmt.NmtDataProvider teren-funkcje-1.0.2-SNAPSHOT.jar diff --git a/src/main/java/pl/wat/ms4ds/terrain/AStar.java b/src/main/java/pl/wat/ms4ds/terrain/AStar.java index 185bec1..e94669a 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/AStar.java +++ b/src/main/java/pl/wat/ms4ds/terrain/AStar.java @@ -87,7 +87,7 @@ public final class AStar { @Override public String toString() { - return "AStarNode{" + + return "AStar.Node{" + "x=" + x + ", y=" + y + ", koncowy=" + koncowy + @@ -153,7 +153,7 @@ public final class AStar { } else { odcinek = wyznaczDroge(start, stop, staryKier, false, podwozie, rodzajDzialania); } - if (odcinek.size() == 0) { + if (odcinek.isEmpty()) { // gdy nie istnieje droga między danymi punktami profilującymi, to zwracam kolekcję pustą return odcinek; } diff --git a/src/main/java/pl/wat/ms4ds/terrain/Bresenham.java b/src/main/java/pl/wat/ms4ds/terrain/Bresenham.java deleted file mode 100644 index 46a9b2f..0000000 --- a/src/main/java/pl/wat/ms4ds/terrain/Bresenham.java +++ /dev/null @@ -1,150 +0,0 @@ -package pl.wat.ms4ds.terrain; - -/** - * Klasa algorytmiczna do wyznaczania kwadratów odcinka (dyskretyzacja odcinka kwadratami/pikselami). - */ -public class Bresenham { - - static int sign(int a) { - if (a > 0) - return 1; - if (a < 0) - return -1; - return 0; - } - - /** - * Metoda generuje tablicę współrzędnych gridowych kolejnych punktów/kwadratów podanego odcinka. - *

- * Wykorzystuje algorytm Bresenhama. - * - * @param x1 współrzędna x początku - * @param y1 współrzędna y początku - * @param x2 współrzędna x końca - * @param y2 współrzędna y końca - * @return tablicę kolejnych kwadratów tworzących odcinek o zadanych końcach - */ - public static Coord.Grid[] generateSegment(int x1, int y1, int x2, int y2) { - int x = x1; - int y = y1; - int dx = x2 - x1; - int dy = y2 - y1; - final int sx = sign(dx); - final int sy = sign(dy); - if (dx < 0) { - dx = -dx; - } - if (dy < 0) { - dy = -dy; - } - final int ddx = 2 * dx; - final int ddy = 2 * dy; - int p; - if (dx >= dy) { - // poruszamy się po x - p = ddy - dx; - Coord.Grid[] res = new Coord.Grid[dx + 1]; - for (int i = 0; i <= dx; i++) { - res[i] = new Coord.Grid(x, y); - if (p > 0) { - y += sy; - p -= ddx; - } - x += sx; - p += ddy; - } - return res; - } else { - // poruszamy sie po y - p = ddx - dy; - Coord.Grid[] res = new Coord.Grid[dy + 1]; - for (int i = 0; i <= dy; i++) { - res[i] = new Coord.Grid(x, y); - if (p > 0) { - x += sx; - p -= ddy; - } - y += sy; - p += ddx; - } - return res; - } - } - - static void print(Coord.Grid[] segment) { - StringBuilder sb = new StringBuilder(300); - sb.append('['); - int last = segment.length - 1; - for (int i = 0; i < segment.length; i++) { - sb.append('('); - sb.append(segment[i].x); - sb.append(','); - sb.append(segment[i].y); - sb.append(')'); - if (i < last) { - sb.append('|'); - } - } - sb.append(']'); - System.out.println(sb); - } - -// public static void main(String[] args) { -// -// var segment = generateSegmentSquares(10, 3, 1, 6); -// var segment2 = generateSegmentSquares(1, 6, 10, 3); -// var line = generateSegmentSquares(2, 1, 5, 8); -// var line1 = generateSegmentSquares(5, 8, 2, 1); -// var line2 = generateSegmentSquares(1, 6, 6, 11); -// var line3 = generateSegmentSquares(11, 6, 6, 1); -// -// final int low = 300; -// final int xHigh = 2000; -// final int yHigh = 1000; -// final int TRIAL_LENGTH = 100; -// System.out.println("Start trial 1"); -// long t0 = System.nanoTime(); -// long sumLen = 0; -// for (int i = 0; i < TRIAL_LENGTH; i++) { -// int x1 = low + RandomStream.uniformInt(-20, 20); -// int y1 = low + RandomStream.uniformInt(-20, 20); -// int x2 = low + RandomStream.uniformInt(-20, 20); -// int y2 = low + RandomStream.uniformInt(-20, 20); -// int dx = Math.abs(x2 - x1); -// int dy = Math.abs(y2 - y1); -// if (dx == dy) { -// continue; -// } -// var seg1 = generateSegmentSquares(x1, y1, x2, y2); -// print(seg1); -// } -// long dt = System.nanoTime() - t0; -// dt /= 1_000_000; -// System.out.println("Czas = " + dt + " [ms]"); -// sumLen = 0; -// System.out.println("Start trial 2"); -// t0 = System.nanoTime(); -// for (int i = 0; i < TRIAL_LENGTH * 10000; i++) { -// int x1 = low + RandomStream.uniformInt(50); -// int y1 = low + RandomStream.uniformInt(50); -// int x2 = 2 * xHigh + RandomStream.uniformInt(1000); -// int y2 = 2 * yHigh + RandomStream.uniformInt(2000); -// int dx = Math.abs(x2 - x1); -// int dy = Math.abs(y2 - y1); -// if (dx == dy) { -// continue; -// } -// -// var seg1 = generateSegmentSquares(x1, y1, x2, y2); -// -// sumLen += seg1.length; -// } -// dt = System.nanoTime() - t0; -// dt /= 1_000_000; -// System.out.println("Czas = " + dt + " [ms] - len=" + sumLen); -// -//// var seg1 = generateSegment(1, 1, 3, 5); -//// var seg2 = generateSegment(14, 4, 3, 8); -//// System.out.println(seg1); -// } -} diff --git a/src/main/java/pl/wat/ms4ds/terrain/Coord.java b/src/main/java/pl/wat/ms4ds/terrain/Coord.java index d74e83a..2b0c4e0 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/Coord.java +++ b/src/main/java/pl/wat/ms4ds/terrain/Coord.java @@ -19,17 +19,14 @@ public class Coord { this.lon = lon; } - public Geo(GeoCoord other) { + public Geo(Geo other) { lat = other.lat; lon = other.lon; } @Override public String toString() { - return "Geo{" + - "lat=" + lat + - ", lon=" + lon + - '}'; + return "Geo{" + "lat=" + lat + ", lon=" + lon + '}'; } } @@ -61,10 +58,7 @@ public class Coord { @Override public String toString() { - return "Puwg{" + - "easting=" + easting + - ", northing=" + northing + - '}'; + return "Puwg{" + "easting=" + easting + ", northing=" + northing + '}'; } } @@ -101,10 +95,7 @@ public class Coord { @Override public String toString() { - return "Grid{" + - "x=" + x + - ", y=" + y + - '}'; + return "Grid{" + "x=" + x + ", y=" + y + '}'; } private static final double ODWROT_SS_DX_MS = 1.0 / MapConsts.SS_DELTA_LON_MS; @@ -264,7 +255,7 @@ public class Coord { * @param lon długość geograficzna * @return współrzędna x klasy GridCoord */ - public static int convertLonTOGridX(double lon) { + public static int convertLonToGridX(double lon) { double xms_f = (lon + 180) * MapConsts.DEG_MS; return zamienWspXmsNaIdKwadratuX((long) xms_f); } @@ -379,8 +370,8 @@ public class Coord { puwgCoord.northing = 999999999999999.0; return; } - double latRad = lat * DEG_2_RAD; - double dlam = (lon - 19.0) * DEG_2_RAD; + double latRad = Math.toRadians(lat); + double dlam = Math.toRadians(lon - 19.0); double dlam_pow_2 = dlam * dlam; double dlam_pow_3 = dlam_pow_2 * dlam; double dlam_pow_4 = dlam_pow_3 * dlam; @@ -405,22 +396,14 @@ public class Coord { double sns = sn * s; t2 = sns * c * OK / 2.0; t3 = sns * c_pow_3 * OK * (5.0 - t_pow_2 + 9.0 * eta + 4.0 * eta_pow_2) / 24.0; - t4 = sns * c_pow_4 * c * OK * (61.0 - 58.0 * t_pow_2 + t_pow_4 - + 270.0 * eta - 330.0 * t_pow_2 * eta + 445.0 * eta_pow_2 - + 324.0 * eta_pow_3 - 680.0 * t_pow_2 * eta_pow_2 - + 88.0 * eta_pow_4 - 600.0 * t_pow_2 * eta_pow_3 - 192.0 * t_pow_2 * eta_pow_4) / 720.0; - t5 = sns * c_pow_4 * c_pow_3 * OK * (1385.0 - 3111.0 * t_pow_2 - + 543.0 * t_pow_4 - t_pow_5 * t) / 40320.0; - puwgCoord.northing = -5300000.0 + t1 + dlam_pow_2 * t2 + dlam_pow_4 * t3 - + dlam_pow_4 * dlam_pow_2 * t4 + dlam_pow_4 * dlam_pow_4 * t5; + t4 = sns * c_pow_4 * c * OK * (61.0 - 58.0 * t_pow_2 + t_pow_4 + 270.0 * eta - 330.0 * t_pow_2 * eta + 445.0 * eta_pow_2 + 324.0 * eta_pow_3 - 680.0 * t_pow_2 * eta_pow_2 + 88.0 * eta_pow_4 - 600.0 * t_pow_2 * eta_pow_3 - 192.0 * t_pow_2 * eta_pow_4) / 720.0; + t5 = sns * c_pow_4 * c_pow_3 * OK * (1385.0 - 3111.0 * t_pow_2 + 543.0 * t_pow_4 - t_pow_5 * t) / 40320.0; + puwgCoord.northing = -5300000.0 + t1 + dlam_pow_2 * t2 + dlam_pow_4 * t3 + dlam_pow_4 * dlam_pow_2 * t4 + dlam_pow_4 * dlam_pow_4 * t5; t1 = sn * c * OK; t2 = sn * c_pow_3 * OK * (1.0 - t_pow_2 + eta) / 6.0; - t3 = sn * c_pow_4 * c * OK * (5.0 - 18.0 * t_pow_2 + t_pow_4 + 14.0 * eta - - 58.0 * t_pow_2 * eta + 13.0 * eta_pow_2 + 4.0 * eta_pow_3 - - 64.0 * t_pow_2 * eta_pow_2 - 24.0 * t_pow_2 * eta_pow_3) / 120.0; + t3 = sn * c_pow_4 * c * OK * (5.0 - 18.0 * t_pow_2 + t_pow_4 + 14.0 * eta - 58.0 * t_pow_2 * eta + 13.0 * eta_pow_2 + 4.0 * eta_pow_3 - 64.0 * t_pow_2 * eta_pow_2 - 24.0 * t_pow_2 * eta_pow_3) / 120.0; t4 = sn * c_pow_4 * c_pow_3 * OK * (61.0 - 479.0 * t_pow_2 + 179.0 * t_pow_4 - t_pow_5 * t) / 5040.0; - puwgCoord.easting = 500000.0 + dlam * t1 + dlam_pow_3 * t2 - + dlam_pow_4 * dlam * t3 + dlam_pow_4 * dlam_pow_3 * t4;// + 0.5; + puwgCoord.easting = 500000.0 + dlam * t1 + dlam_pow_3 * t2 + dlam_pow_4 * dlam * t3 + dlam_pow_4 * dlam_pow_3 * t4;// + 0.5; } /** @@ -463,31 +446,23 @@ public class Coord { double t0, t1, t2, t3; t0 = t / (2.0 * sr * sn * OK_POW_2); t1 = t * (5.0 + 3.0 * t_pow_2 + eta - 4.0 * eta_pow_2 - 9.0 * t_pow_2 * eta) / (24.0 * sr * sn_pow_3 * OK_POW_4); - t2 = t * (61.0 + 90.0 * t_pow_2 + 46.0 * eta + 45.0 * t_pow_4 - 252.0 * t_pow_2 * eta - 3.0 * eta_pow_2 - + 100.0 * eta_pow_3 - 66.0 * t_pow_2 * eta_pow_2 - 90.0 * t_pow_4 * eta + 88.0 * eta_pow_4 - + 225.0 * t_pow_4 * eta_pow_2 + 84.0 * t_pow_2 * eta_pow_3 - 192.0 * t_pow_2 * eta_pow_4) / (720.0 * sr * sn_pow_5 * OK_POW_6); - t3 = t * (1385.0 + 3633 * t_pow_2 + 4095.0 * t_pow_4 + 1575.0 * t_pow_6) / (40320 * sr * sn_pow_7 * (OK_POW_8)); - geoCoord.lat = ftphi - de_pow_2 * t0 + de_pow_4 * t1 - de_pow_3 * de_pow_3 * t2 + de_pow_4 * de_pow_3 * t3; + t2 = t * (61.0 + 90.0 * t_pow_2 + 46.0 * eta + 45.0 * t_pow_4 - 252.0 * t_pow_2 * eta - 3.0 * eta_pow_2 + 100.0 * eta_pow_3 - 66.0 * t_pow_2 * eta_pow_2 - 90.0 * t_pow_4 * eta + 88.0 * eta_pow_4 + 225.0 * t_pow_4 * eta_pow_2 + 84.0 * t_pow_2 * eta_pow_3 - 192.0 * t_pow_2 * eta_pow_4) / (720.0 * sr * sn_pow_5 * OK_POW_6); + t3 = t * (1385.0 + 3633.0 * t_pow_2 + 4095.0 * t_pow_4 + 1575.0 * t_pow_6) / (40320.0 * sr * sn_pow_7 * (OK_POW_8)); + double latrad = ftphi - de_pow_2 * t0 + de_pow_4 * t1 - de_pow_3 * de_pow_3 * t2 + de_pow_4 * de_pow_3 * t3; t0 = 1.0 / (sn * c * OK); t1 = (1.0 + 2.0 * t_pow_2 + eta) / (6.0 * sn_pow_3 * c * (OK_POW_3)); - t2 = (5.0 + 6.0 * eta + 28.0 * t_pow_2 - 3.0 * eta_pow_2 + 8.0 * t_pow_2 * eta - + 24.0 * t_pow_4 - 4.0 * eta_pow_3 + 4.0 * t_pow_2 * eta_pow_2 - + 24.0 * t_pow_2 * eta_pow_3) / (120.0 * sn_pow_5 * c * (OK_POW_5)); + t2 = (5.0 + 6.0 * eta + 28.0 * t_pow_2 - 3.0 * eta_pow_2 + 8.0 * t_pow_2 * eta + 24.0 * t_pow_4 - 4.0 * eta_pow_3 + 4.0 * t_pow_2 * eta_pow_2 + 24.0 * t_pow_2 * eta_pow_3) / (120.0 * sn_pow_5 * c * (OK_POW_5)); t3 = (61.0 + 662.0 * t_pow_2 + 1320.0 * t_pow_4 + 720.0 * t_pow_6) / (5040.0 * sn_pow_7 * c * OK_POW_7); double dlam = de * t0 - de_pow_3 * t1 + de_pow_3 * de_pow_2 * t2 - de_pow_3 * de_pow_4 * t3; // 19.0 * DEG_2_RAD == 0.33161255787892263; // geoCoord.lon = 0.33161255787892263 + dlam; // geoCoord.lon *= RAD_2_DEG; - geoCoord.lon = dlam * RAD_2_DEG; - geoCoord.lon += 19.0; - geoCoord.lat *= RAD_2_DEG; + geoCoord.lon = Math.toDegrees(dlam) + 19.0; + geoCoord.lat = Math.toDegrees(latrad); } - //////////////////////////////////////////////////////////////////////////////// + /// ////////////////////////////////////////////////////////////////////////////// // Funkcje pomocnicze i stałe - - /// ///////////////////////////////////////////////////////////////////////////// - static double calculateESquared(double a, double b) { a *= a; b *= b; @@ -516,13 +491,9 @@ public class Coord { } static double sphtmd(double sphi) { - return (AP * sphi) - (BP * Math.sin(2.0 * sphi)) + (CP * Math.sin(4.0 * sphi)) - - (DP * Math.sin(6.0 * sphi)) + (EP * Math.sin(8.0 * sphi)); + return (AP * sphi) - (BP * Math.sin(2.0 * sphi)) + (CP * Math.sin(4.0 * sphi)) - (DP * Math.sin(6.0 * sphi)) + (EP * Math.sin(8.0 * sphi)); } - private static final double DEG_2_RAD = Math.PI / 180.0; - private static final double RAD_2_DEG = 180.0 / Math.PI; - /** * Dlługość dużej półsi, w metrach dla elipsoidy WGS-84. */ diff --git a/src/main/java/pl/wat/ms4ds/terrain/CoordConversion.java b/src/main/java/pl/wat/ms4ds/terrain/CoordConversion.java deleted file mode 100644 index bc53056..0000000 --- a/src/main/java/pl/wat/ms4ds/terrain/CoordConversion.java +++ /dev/null @@ -1,158 +0,0 @@ -package pl.wat.ms4ds.terrain; - -public class CoordConversion { - - /* - Opis: - konwersja wspolrzednych z ukladu WGS 84 do ukladu PUWG 1992 - Parametry: - B_stopnie - szerokosc geograficzna wyrazona w stopniach - L_stopnie - dlugosc geograficzna wyrazona w stopniach - Xpuwg - wskazanie na wspolrzedna X ukladu PUWG 1992 (UWAGA - wspolrzedna pionowa) - Ypuwg - wskazanie na wspolrzedna Y ukladu PUWG 1992 (UWAGA - wspolrzedna pozioma) - Zwracana wartosc: - 0 - konwersja powiodla sie - 1 - szerokosc geograficzna B poza zakresem - 2 - dlugosc geograficzna L poza zakresem - */ - public static int wgs84ToPuwg1992(double B_stopnie, double L_stopnie) { - double Xpuwg; - double Ypuwg; - // Parametry elipsoidy GRS-80 - double e = 0.0818191910428; //pierwszymimo¶ród elipsoidy - double R0 = 6367449.14577; //promieñ sfery Lagrange.a - double Snorm = 2.0E-6; //parametr normuj±cy - double xo = 5760000.0; //parametr centruj±cy - - //Wspolczynniki wielomianu - double a0 = 5765181.11148097; - double a1 = 499800.81713800; - double a2 = -63.81145283; - double a3 = 0.83537915; - double a4 = 0.13046891; - double a5 = -0.00111138; - double a6 = -0.00010504; - - // Parametry odwzorowania Gaussa-Kruegera dla uk³adu PUWG92 - double L0_stopnie = 19.0; //Pocz±tek uk³adu wsp. PUWG92 (d³ugo¶æ) - double m0 = 0.9993; - double x0 = -5300000.0; - double y0 = 500000.0; - - // Zakres stosowalnosci metody - double Bmin = 48.0 * Math.PI / 180.0; - double Bmax = 56.0 * Math.PI / 180.0; - double dLmin = -6.0 * Math.PI / 180.0; - double dLmax = 6.0 * Math.PI / 180.0; - - // Weryfikacja danych wejsciowych - double B = B_stopnie * Math.PI / 180.0; - double dL_stopnie = L_stopnie - L0_stopnie; - double dL = dL_stopnie * Math.PI / 180.0; - - if ((B < Bmin) || (B > Bmax)) - return 1; - - if ((dL < dLmin) || (dL > dLmax)) - return 2; - - //etap I - elipsoida na kulê - double U = 1.0 - e * Math.sin(B); - double V = 1.0 + e * Math.sin(B); - double K = Math.pow((U / V), (e / 2.0)); - double C = K * Math.tan(B / 2.0 + Math.PI / 4.0); - double fi = 2.0 * Math.atan(C) - Math.PI / 2.0; - double d_lambda = dL; - - // etap II - kula na walec - double p = Math.sin(fi); - double q = Math.cos(fi) * Math.cos(d_lambda); - double r = 1.0 + Math.cos(fi) * Math.sin(d_lambda); - double s = 1.0 - Math.cos(fi) * Math.sin(d_lambda); - double XMERC = R0 * Math.atan(p / q); - double YMERC = 0.5 * R0 * Math.log(r / s); - -// //etap III - walec na p³aszczyznê -// complex Z ((XMERC - xo) * Snorm, YMERC * Snorm); -// complex Zgk; -// Zgk = a0 + Z * (a1 + Z * (a2 + Z * (a3 + Z * (a4 + Z * (a5 + Z * a6))))); -// double Xgk = Zgk.real(); -// double Ygk = Zgk.imag(); -// -// //Przej¶cie do uk³adu aplikacyjnego -// Xpuwg = m0 * Xgk + x0; -// Ypuwg = m0 * Ygk + y0; - - return 0; - } - - /* -Opis: - konwersja wspolrzednych z ukladu PUWG 1992 do ukladu WGS 84 -Parametry: - Xpuwg - wskazanie na wspolrzedna X ukladu PUWG 1992 (UWAGA - wspolrzedna pionowa) - Ypuwg - wskazanie na wspolrzedna Y ukladu PUWG 1992 (UWAGA - wspolrzedna pozioma) - B_stopnie - szerokosc geograficzna wyrazona w stopniach - L_stopnie - dlugosc geograficzna wyrazona w stopniach -Zwracana wartosc: - 0 - konwersja powiodla sie - */ - public static int puwg1992ToWgs84(double Xpuwg, double Ypuwg, double B_stopnie, double L_stopnie) { - double L0_stopnie = 19.0; //Pocz±tek uk³adu wsp. PUWG92 (d³ugo¶æ) - double m0 = 0.9993; - double x0 = -5300000.0; - double y0 = 500000.0; - - double R0 = 6367449.14577; //promieñ sfery Lagrange.a - double Snorm = 2.0E-6; //parametr normuj±cy - double xo_prim = 5765181.11148097; //parametr centruj±cy - - // Wspolczynniki wielomianu - double b0 = 5760000; - double b1 = 500199.26224125; - double b2 = 63.88777449; - double b3 = -0.82039170; - double b4 = -0.13125817; - double b5 = 0.00101782; - double b6 = 0.00010778; - - // Wspolczynniki szeregu tryg. - double c2 = 0.0033565514856; - double c4 = 0.0000065718731; - double c6 = 0.0000000176466; - double c8 = 0.0000000000540; - - //Przejscie z ukladu aplikacyjnego - double Xgk, Ygk; - Xgk = (Xpuwg - x0) / m0; - Ygk = (Ypuwg - y0) / m0; - -// //etap I - (Xgk, Ygk) -> (Xmerc, Ymerc) -// complex Z ((Xgk - xo_prim) * Snorm, Ygk * Snorm); -// complex Zmerc; -// -// Zmerc = b0 + Z * (b1 + Z * (b2 + Z * (b3 + Z * (b4 + Z * (b5 + Z * b6))))); -// -// double Xmerc = Zmerc.real(); -// double Ymerc = Zmerc.imag(); -// -// //etap II - Xmerc,Ymerc -> fi, delta_lambda -// double alfa = Xmerc / R0; -// double beta = Ymerc / R0; -// -// double w = 2.0 * atan(exp(beta)) - M_PI / 2.0; -// double fi = asin(cos(w) * sin(alfa)); -// double d_lambda = atan(tan(w) / cos(alfa)); -// -// //etap III -// double B = fi + c2 * sin(2.0 * fi) + c4 * sin(4.0 * fi) + c6 * sin(6.0 * fi) + c8 * sin(8.0 * fi); -// double dL = d_lambda; -// -// //Obliczenia koncowe -// *B_stopnie = B / M_PI * 180.0; -// double dL_stopnie = dL / M_PI * 180.0; -// *L_stopnie = dL_stopnie + L0_stopnie; - - return 0; - } -} diff --git a/src/main/java/pl/wat/ms4ds/terrain/GeoCoord.java b/src/main/java/pl/wat/ms4ds/terrain/GeoCoord.java deleted file mode 100644 index 99c2759..0000000 --- a/src/main/java/pl/wat/ms4ds/terrain/GeoCoord.java +++ /dev/null @@ -1,19 +0,0 @@ -package pl.wat.ms4ds.terrain; - -/** - * - */ -public class GeoCoord { - public double lat; - public double lon; - - public GeoCoord() {} - public GeoCoord(double lat, double lon) { - this.lat = lat; - this.lon = lon; - } - public GeoCoord(GeoCoord other) { - lat = other.lat; - lon = other.lon; - } -} diff --git a/src/main/java/pl/wat/ms4ds/terrain/GeomUtils.java b/src/main/java/pl/wat/ms4ds/terrain/GeomUtils.java index bb79893..e9230bd 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/GeomUtils.java +++ b/src/main/java/pl/wat/ms4ds/terrain/GeomUtils.java @@ -4,8 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.wat.ms4ds.common.EGeoDirection; -import java.util.ArrayList; - public class GeomUtils { private static final Logger logger = LoggerFactory.getLogger(GeomUtils.class); @@ -21,7 +19,7 @@ public class GeomUtils { * @param p Wsp poczatku wektora * @param q Wsp konca wektora * @param r Wsp testowanego punktu - * @return det3 == 0, gdy punkt jest wpolliniowy, det3 wieksze od 0, gdy punkt lezy po lewej stronie wektora, + * @return det3 == 0, gdy punkt jest wspolliniowy, det3 wieksze od 0, gdy punkt lezy po lewej stronie wektora, * det3 mniejsze od 0, gdy punkt lezy po prawej stronie wektora, */ public static long det3(Coord.Grid p, Coord.Grid q, Coord.Grid r) { @@ -77,11 +75,11 @@ public class GeomUtils { * @return true, jesli punkt r lezy na odcinku */ public static boolean include(int px, int py, int qx, int qy, int rx, int ry) { - return det3(px, py, qx, qy, rx, ry) == 0 && - Math.min(px, qx) <= rx && + return Math.min(px, qx) <= rx && rx <= Math.max(px, qx) && Math.min(py, qy) <= ry && - ry <= Math.max(py, qy); + ry <= Math.max(py, qy) && + det3(px, py, qx, qy, rx, ry) == 0; } /** @@ -228,147 +226,91 @@ public class GeomUtils { } /** - * Sprawdza, czy punkt należy do wielokata w ogólnym przypadku (boki wielokata bez samoprzecięć). + * Wyznacza ograniczenia dolne i górne współrzędnych: xMin, yMin, xMax, yMax. * - * @param polygon Kolejne wierzchołki wielokata. - * @param p Testowany punkt. - * @return true, jeśli testowany punkt leży wewnątrz wielokąta + * @param vertices + * @return vertices bounds: 0 - xMin, 1 - yMin, 2 - xMax, 3 - yMax */ - public static boolean insidePolygon(Coord.Grid[] polygon, Coord.Grid p) { - // sprawdzenie czy punkt p należy do jednego z boków wielokąta - for (int i = 0; i < polygon.length; i++) { - int i_plus_1 = (i + 1) % polygon.length; - if (include(polygon[i], polygon[i_plus_1], p)) { - return true; - } + public static int[] getBounds(Coord.Grid[] vertices) { + int xMin = Integer.MAX_VALUE; + int yMin = Integer.MAX_VALUE; + int xMax = Integer.MIN_VALUE; + int yMax = Integer.MIN_VALUE; + for (Coord.Grid v : vertices) { + xMin = Math.min(xMin, v.x); + yMin = Math.min(yMin, v.y); + xMax = Math.max(xMax, v.x); + yMax = Math.max(yMax, v.y); } - // punkty p i p1 wyznaczają półprostą równoległą do osi OX - // współrzędna X punktu p1 musi być większa od największej - // współrzędnej X wśród wszystkich wierzchołków wielokąta - Coord.Grid p1 = new Coord.Grid(p.x + 1, p.y); - for (int i = 0; i < polygon.length; i++) { - p1.x = Math.max(polygon[i].x, p1.x); - } - p1.x++; - // licznik przecięć - int cross_count = 0; - // przechodzenie po wszystkich bokach - int i = 0; - while (i < polygon.length) { - int i_plus_1 = (i + 1) % polygon.length; - int i_plus_2 = (i + 2) % polygon.length; - int i_minus_1 = (i + polygon.length - 1) % polygon.length; - int sgn_det3_i_minus_1 = sgn(det3(p, p1, polygon[i_minus_1])); - int sgn_det3_i_plus_1 = sgn(det3(p, p1, polygon[i_plus_1])); - int sgn_det3_i_plus_2 = sgn(det3(p, p1, polygon[i_plus_2])); - if (includeHorizontaly(p, p1, polygon[i])) { - // polprosta p-p1 zawiera wierzchołek w(i) - if (includeHorizontaly(p, p1, polygon[i_plus_1])) { - // polprosta p-p1 zawiera bok w(i)w(i+1) - if (sgn_det3_i_minus_1 != sgn_det3_i_plus_2) { - // punkt wczesniejszy w(i-1) i dalszy w(i+2) testowanego boku wielokata - // leza po przeciwnych stronach polprostej p-p1, zatem ilosc przeciec +1 - cross_count++; - } - // pomijamy nastepny wierzcholek - i++; - } else { - // polprosta p-p1 zawiera TYLKO wierzchołek w(i) - if (sgn_det3_i_minus_1 != 0 && sgn_det3_i_minus_1 != sgn_det3_i_plus_1) { - // wierzcholki sasiadujace z wierzchołkiem w(i) tj. w(i-1) i w(i+1) - // leza po przeciwnych stronach polprostej p-p1, zatem ilosc przeciec +1 - cross_count++; - } - } - } else { - // sprawdzenie czy polprosta p-p1 - // przecina bok w(i)w(i+1) wielokąta wewnatrz odcinka - if (cross(p, p1, polygon[i], polygon[i_plus_1])) { - cross_count++; - } - } - i++; - } - // obliczenie wyniku (nieparzysty oznacza należenie punktu do wielokata) - return (cross_count % 2) == 1; + return new int[]{xMin, yMin, xMax, yMax}; } /** - * Sprawdza, czy punkt należy do wielokata w ogólnym przypadku (boki wielokata bez samoprzecięć). + * Funkcja bada zawieranie się punktu w ramach wielokąta. * - * @param polygon Kolejne wierzchołki wielokata. - * @param px Wsp. x testowanego punktu. - * @param py Wsp. y testowanego punktu. - * @return true, jeśli testowany punkt leży wewnątrz wielokąta + * @param polygon + * @param x + * @param y + * @return */ - public static boolean insidePolygon(Coord.Grid[] polygon, int px, int py) { - // sprawdzenie czy punkt p nie należy do jednego z boków wielokąta + public static boolean insidePolygon(Coord.Grid[] polygon, int x, int y) { + // Sprawdzenie należenia punktu (x, y) do dowolnej krawędzi wielokąta. + // for (int i = 0; i < polygon.length; i++) { - int i_plus_1 = (i + 1) % polygon.length; - if (include(polygon[i].x, polygon[i].y, polygon[i_plus_1].x, polygon[i_plus_1].y, px, py)) { + int x1 = polygon[i].x; + int y1 = polygon[i].y; + int ii = (i + 1) % polygon.length; + int x2 = polygon[ii].x; + int y2 = polygon[ii].y; + if (include(x1, y1, x2, y2, x, y)) { return true; } } - // punkty p i p1 wyznaczają półprostą równoległą do osi OX - // współrzędna X punktu p1 musi być większa od największej - // współrzędnej X wśród wszystkich wierzchołków wielokąta - Coord.Grid p1 = new Coord.Grid(px + 1, py); + // Sprawdzenie należenia punktu (x, y) do wnętrza wielokąta. Zliczanie liczby przecięć promienia idącego od + // punktu w prawo równolegle do osi OX. + // Nieparzysta liczba oznacza należenie. + // + int count = 0; for (int i = 0; i < polygon.length; i++) { - p1.x = Math.max(polygon[i].x, p1.x); - } - p1.x++; - // licznik przecięć - int cross_count = 0; - // przechodzenie po wszystkich bokach - int i = 0; - while (i < polygon.length) { - int i_plus_1 = (i + 1) % polygon.length; - int i_plus_2 = (i + 2) % polygon.length; - int i_minus_1 = (i + polygon.length - 1) % polygon.length; - int sgn_det3_i_minus_1 = sgn(det3(px, py, p1.x, p1.y, polygon[i_minus_1].x, polygon[i_minus_1].y)); - if (includeHorizontaly(px, py, p1.x, p1.y, polygon[i].x, polygon[i].y)) { - // polprosta p-p1 zawiera wierzchołek w(i) - if (includeHorizontaly(px, py, p1.x, p1.y, polygon[i_plus_1].x, polygon[i_plus_1].y)) { - int sgn_det3_i_plus_2 = sgn(det3(px, py, p1.x, p1.y, polygon[i_plus_2].x, polygon[i_plus_2].y)); - // polprosta p-p1 zawiera bok w(i)w(i+1) - if (sgn_det3_i_minus_1 != sgn_det3_i_plus_2) { - // punkt wczesniejszy w(i-1) i dalszy w(i+2) testowanego boku wielokata - // leza po przeciwnych stronach polprostej p-p1, zatem ilosc przeciec +1 - cross_count++; + int x1 = polygon[i].x; + int y1 = polygon[i].y; + int ii = (i + 1) % polygon.length; + int x2 = polygon[ii].x; + int y2 = polygon[ii].y; + if (y < y1 != y < y2) { + int dy = y2 - y1; + int a = (x - x1) * dy; + int b = (y - y1) * (x2 - x1); + if (dy >= 0) { + if (a < b) { + count++; } - // pomijamy nastepny wierzcholek - i++; } else { - int sgn_det3_i_plus_1 = sgn(det3(px, py, p1.x, p1.y, polygon[i_plus_1].x, polygon[i_plus_1].y)); - // polprosta p-p1 zawiera TYLKO wierzchołek w(i) - if (sgn_det3_i_minus_1 != 0 && sgn_det3_i_minus_1 != sgn_det3_i_plus_1) { - // wierzcholki sasiadujace z wierzchołkiem w(i) tj. w(i-1) i w(i+1) - // leza po przeciwnych stronach polprostej p-p1, zatem ilosc przeciec +1 - cross_count++; + if (b < a) { + count++; } } - } else { - // sprawdzenie czy polprosta p-p1 - // przecina bok w(i)w(i+1) wielokąta wewnatrz odcinka - if (cross(px, py, p1.x, p1.y, polygon[i].x, polygon[i].y, polygon[i_plus_1].x, polygon[i_plus_1].y)) { - cross_count++; - } } - i++; } - // obliczenie wyniku (nieparzysty oznacza należenie punktu do wielokata) - return (cross_count % 2) == 1; + return (count % 2) == 1; } /** - * Wyznacza kierunek przeciwny dla zadanego, przy założeniu, że kierunki geograficzne numerowane są od 0 do 7 + * Funkcja bada należenie punktu do prostokąta zorientowanego równolegle do osi współrzędnych. * - * @param kierunek zadany kierunek - * @return kierunek przeciwny dla zadanego + * @param xMin minimalna współrzędna X prostokąta (lewego dolnego rogu) + * @param yMin minimalna współrzędna Y prostokąta (lewego dolnego rogu) + * @param xMax maksymalna współrzędna X prostokąta (prawego górnego rogu) + * @param yMax maksymalna współrzędna Y prostokąta (prawego górnego rogu) + * @param xTest współrzędna X punktu testowanego + * @param yTest współrzędna Y punktu testowanego + * @return true, jeśli testowany punkt należy do prostokąta */ - public static int kierunekPrzeciwny(int kierunek) { - int kierPrzeciwny = (kierunek + 4) % 8; - return kierPrzeciwny; + public static boolean insideRectangle(int xMin, int yMin, int xMax, int yMax, int xTest, int yTest) { + if (xTest < xMin || xTest > xMax) { + return false; + } + return yTest >= yMin && yTest <= yMax; } // tangens kąta alfa = 22.5 stopnia @@ -383,7 +325,7 @@ public class GeomUtils { * @param stop kwadrat końcowy wektora * @return kierunek geograficzny klasy EGeoDirection */ - public static EGeoDirection kierunek(Coord.Grid start, Coord.Grid stop) { + public static EGeoDirection estimateDirection(Coord.Grid start, Coord.Grid stop) { if (start.x == stop.x && start.y == stop.y) { return EGeoDirection.UNDEFINED; } @@ -437,6 +379,20 @@ public class GeomUtils { } } + private static final EGeoDirection[][] NEIGHBOR_DIRECTIONS = new EGeoDirection[][]{ + {EGeoDirection.SOUTHWEST, EGeoDirection.WEST, EGeoDirection.NORTHWEST}, + {EGeoDirection.SOUTH, EGeoDirection.UNDEFINED, EGeoDirection.NORTH}, + {EGeoDirection.SOUTHEAST, EGeoDirection.EAST, EGeoDirection.NORTHEAST}, + }; + + public static EGeoDirection neighborDirection(int xStart, int yStart, int xEnd, int yEnd) { + // Przesuwam różnice współrzędnych o jeden tzn. {-1, 0, 1} -> {0, 1, 2}, aby użyć do indeksowania. + // + int dx = xEnd - xStart + 1; + int dy = yEnd - yStart + 1; + return NEIGHBOR_DIRECTIONS[dx][dy]; + } + /** * Funkcja zwraca kierunek geograficzny dla wektora opisanego na sasiednich kwadratach. * @@ -554,681 +510,337 @@ public class GeomUtils { } /** - * Funkcja wyznacza tablicę kwadratów leżących na łamanej. + * Metoda generuje tablicę współrzędnych gridowych kolejnych punktów/kwadratów podanego odcinka. + *

+ * Wykorzystuje algorytm Bresenhama. * - * @param punktyLamanej tablica wspolrzednych tworzących łamaną - * @return kwadratyWyj kolekcja kwadratów leżących na łamanej + * @param x1 współrzędna x początku + * @param y1 współrzędna y początku + * @param x2 współrzędna x końca + * @param y2 współrzędna y końca + * @return tablicę kolejnych kwadratów tworzących odcinek o zadanych końcach */ - public static ArrayList kwadratyLamanej(Coord.Grid[] punktyLamanej) { - ArrayList kwadratyWyj = new ArrayList(); - if (punktyLamanej.length == 2) { - Coord.Grid[] kwadraty = kwadratyOdcinka(punktyLamanej[0], punktyLamanej[1]); - for (int i = 0; i < kwadraty.length; i++) { - kwadratyWyj.add(kwadraty[i]); - } - return kwadratyWyj; + public static Coord.Grid[] generateSquaresOfSegment(int x1, int y1, int x2, int y2) { + int x = x1; + int y = y1; + int dx = x2 - x1; + int dy = y2 - y1; + final int sx = Integer.compare(dx, 0); + final int sy = Integer.compare(dy, 0); + if (dx < 0) { + dx = -dx; } - for (int i = 0; i < punktyLamanej.length - 1; i++) { - Coord.Grid[] kwadraty = kwadratyOdcinka(punktyLamanej[i], punktyLamanej[i + 1]); - int lko; - if (i < punktyLamanej.length - 2) { - // odcinek lamanej nie jest ostatni, zatem bez ostatniego kwadratu odcinka, - // gdyz powtorzy sie jako pierwszy w nastepnym odcinku lamanej - lko = kwadraty.length - 1; - } else { - lko = kwadraty.length; - } - for (int j = 0; j < lko; j++) { - kwadratyWyj.add(kwadraty[j]); - } + if (dy < 0) { + dy = -dy; } - return kwadratyWyj; + final int ddx = 2 * dx; + final int ddy = 2 * dy; + int p; + if (dx >= dy) { + // poruszamy się po x + p = ddy - dx; + Coord.Grid[] res = new Coord.Grid[dx + 1]; + for (int i = 0; i <= dx; i++) { + res[i] = new Coord.Grid(x, y); + if (p > 0) { + y += sy; + p -= ddx; + } + x += sx; + p += ddy; + } + return res; + } else { + // poruszamy sie po y + p = ddx - dy; + Coord.Grid[] res = new Coord.Grid[dy + 1]; + for (int i = 0; i <= dy; i++) { + res[i] = new Coord.Grid(x, y); + if (p > 0) { + x += sx; + p -= ddy; + } + y += sy; + p += ddx; + } + return res; + } + } + + public static int[] generateSquaresOfSegment2(int x1, int y1, int x2, int y2) { + int x = x1; + int y = y1; + int dx = x2 - x1; + int dy = y2 - y1; + final int sx = Integer.compare(dx, 0); + final int sy = Integer.compare(dy, 0); + if (dx < 0) { + dx = -dx; + } + if (dy < 0) { + dy = -dy; + } + final int ddx = 2 * dx; + final int ddy = 2 * dy; + int p; + int size; + int[] res; + if (dx >= dy) { + // poruszamy się po x + p = ddy - dx; + // rozmiar tablicy 2 razy liczba współrzędnych + size = (dx + 1) << 1; + res = new int[size]; + for (int i = 0; i < size; ) { + res[i++] = x; + res[i++] = y; + if (p > 0) { + y += sy; + p -= ddx; + } + x += sx; + p += ddy; + } + return res; + } else { + // poruszamy sie po y + p = ddx - dy; + // rozmiar tablicy 2 razy liczba współrzędnych + size = (dy + 1) << 1; + res = new int[size]; + for (int i = 0; i < size; ) { + res[i++] = x; + res[i++] = y; + if (p > 0) { + x += sx; + p -= ddy; + } + y += sy; + p += ddx; + } + return res; + } + } + + static void test(int... args) { + int z = args[0]; } /** * Funkcja wyznacza tablicę kwadratów leżących na łamanej. * - * @param punktyLamanej tablica wspolrzednych odcinków tworzących łamaną + * @param vertices tablica wspolrzednych wierzcholkow tworzacych lamana * @return tablica (ciąg) sąsiednich kwadratów leżących na łamanej */ - public static Coord.Grid[] kwadratyLamanej2(Coord.Grid[] punktyLamanej) { - Coord.Grid[] kwadratyWyj; - if (punktyLamanej.length == 2) { - kwadratyWyj = kwadratyOdcinka(punktyLamanej[0], punktyLamanej[1]); - return kwadratyWyj; + public static Coord.Grid[] generateSquaresOfSegments(Coord.Grid[] vertices) { + if (vertices.length == 2) { + return generateSquaresOfSegment(vertices[0].x, vertices[0].y, vertices[1].x, vertices[1].y); } - int maxLen = 0; - for (int i = 0; i < punktyLamanej.length - 1; i++) { - maxLen += Math.abs(punktyLamanej[i].x - punktyLamanej[i + 1].x) + Math.abs(punktyLamanej[i].y - punktyLamanej[i + 1].y) + 1; - } - Coord.Grid[] helpTab = new Coord.Grid[maxLen]; - int lastLen = 0; - for (int i = 0; i < punktyLamanej.length - 1; i++) { - Coord.Grid[] kwadraty = kwadratyOdcinka(punktyLamanej[i], punktyLamanej[i + 1]); - // liczba kwadratów bieżącego odcinka do zapamietania - int lko; - if (i < punktyLamanej.length - 2) { - // odcinek lamanej nie jest ostatni, zatem bez ostatniego kwadratu odcinka, - // gdyz powtorzy sie jako pierwszy w nastepnym odcinku lamanej - lko = kwadraty.length - 1; + Coord.Grid[][] tempSegments = new Coord.Grid[vertices.length - 1][]; + int size = 0; + for (int i = 0; i < tempSegments.length; i++) { + tempSegments[i] = generateSquaresOfSegment(vertices[i].x, vertices[i].y, vertices[i + 1].x, vertices[i + 1].y); + if (i == 0) { + // będą kopiowane wszystkie kwadraty + size += tempSegments[i].length; } else { - // ostatni odcinek łamanej, zatem wszystkie kwadraty odcinka - lko = kwadraty.length; - } - System.arraycopy(kwadraty, 0, helpTab, lastLen, lko); -// for (int j = 0; j < lko; j++) { -// helpTab[lastLen] = kwadraty[j]; -// lastLen++; -// } - } - kwadratyWyj = new Coord.Grid[lastLen]; - System.arraycopy(helpTab, 0, kwadratyWyj, 0, lastLen); -// for (int i = 0; i < lastLen; i++) { -// kwadratyWyj[i] = helpTab[i]; -// } - return kwadratyWyj; - } - - /** - * Funkcja wyznacza tablice (ciąg) sąsiednich kwadratow na odcinku zadanym przez wspolrzedne jego koncow. - * - * @param pocz wspolrzedna poczatku odcinka - * @param kon wspolrzedna konca odcinka - * @return tablica sąsiednich kwadratow lezacych na odcinku - */ - public static Coord.Grid[] kwadratyOdcinka(Coord.Grid pocz, Coord.Grid kon) { - return kwadratyOdcinka(pocz.x, pocz.y, kon.x, kon.y); - } - - /** - * Funkcja wyznacza tablice (ciąg) sąsiednich kwadratow na odcinku zadanym przez wspolrzedne jego koncow. - * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica sąsiednich kwadratow lezacych na odcinku - */ - public static Coord.Grid[] kwadratyOdcinka(int xp, int yp, int xk, int yk) { - Coord.Grid[] kwadraty; - if ((xp == xk) && (yp == yk)) { - // odcinek skladajacy sie z jednego kwadratu - kwadraty = new Coord.Grid[1]; - kwadraty[0] = new Coord.Grid(xp, yp); - return kwadraty; - } - if (xp == xk) { - // odcinek pionowy - kwadraty = kwadratyOdcinkaPionowego(xp, yp, xk, yk); - return kwadraty; - } - if (yp == yk) { - // odcinek poziomy - kwadraty = kwadratyOdcinkaPoziomego(xp, yp, xk, yk); - return kwadraty; - } - int dx = Math.abs(xk - xp); - int dy = Math.abs(yk - yp); - if (dx == dy) { - // odcinek na przekatnej duzego kwadratu - kwadraty = kwadratyOdcinkaNaPrzekatnej(xp, yp, xk, yk); - } else { - // odcinek na przekatnej duzego prostokata - kwadraty = kwadratyOdcinkaDowolnego(xp, yp, xk, yk); - } - return kwadraty; - } - - /** - * Funkcja pomocnicza. - * Funkcja zwraca kwadraty odcinka/wektora zorientowanego względem osi OX pod katem 90 stop. lub 270 stop. - * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica kwadratow lezacych na odcinku - */ - private static Coord.Grid[] kwadratyOdcinkaPionowego(int xp, int yp, int xk, int yk) { - Coord.Grid[] kwadraty = new Coord.Grid[Math.abs(yp - yk) + 1]; - if (yp < yk) { - // zorientowanie wektora do gory - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp, yp + i); - } - } else { - // zorientowanie wektora do dolu - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp, yp - i); + // kopiowane wszystkie poza pierwszym + size += tempSegments[i].length - 1; } } - return kwadraty; - } - - /** - * Funkcja pomocnicza. - * Funkcja zwraca kwadraty odcinka/wektora zorientowanego względem osi OX pod katem 0 stop. lub 180 stop.. - * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica kwadratow lezacych na odcinku - */ - private static Coord.Grid[] kwadratyOdcinkaPoziomego(int xp, int yp, int xk, int yk) { - Coord.Grid[] kwadraty = new Coord.Grid[Math.abs(xp - xk) + 1]; - if (xp < xk) { - // zorientowanie wektora w prawo - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp + i, yp); - } - } else { - // zorientowanie wektora w lewo - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp - i, yp); - } - } - return kwadraty; - } - - /** - * Funkcja pomocnicza. - * Zwraca kwadraty leżące na odcinku/wektorze zorientowanym względem osi OX pod kątem innym niż: 0, 90, 180, 270. - * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica kwadratow lezacych na odcinku - */ - private static Coord.Grid[] kwadratyOdcinkaDowolnego(int xp, int yp, int xk, int yk) { - int x = xp; - int y = yp; - int dx = xk - xp; - int dy = yk - yp; - Coord.Grid[] tab; - int abs_dx = Math.abs(dx); - int abs_dy = Math.abs(dy); - if (abs_dx > abs_dy) { - // przypadek, gdy poruszamy sie po osi OX - // wyznaczenie wspolczynnikow prostej zawierającej odcinek na podstawie środków kwadratów - double a = (double) dy / (double) dx; - double b = yp + 0.5 - a * (xp + 0.5); - tab = new Coord.Grid[abs_dx + 1]; - int i = 0; - tab[i] = new Coord.Grid(x, y); - if (dx > 0) { - // idziemy w prawo w kierunku rosnacych wartosci x - if (dy > 0) { - // prosta zaierająca odcinek jest funkcją rosnącą - while (x < xk) { - x++; - int yy_ref = y + 1; - double yy_l_test = a * x + b; - double yy_h_test = a * (x + 1) + b; - double dy_l = Math.abs(yy_ref - yy_l_test); - double dy_h = Math.abs(yy_ref - yy_h_test); - if (dy_l <= dy_h) { - y++; - } - i++; - tab[i] = new Coord.Grid(x, y); - } - } else { - // prosta zawierająca odcinek jest funkcją malejącą - while (x < xk) { - x++; - int yy_ref = y; - double yy_l_test = a * x + b; - double yy_h_test = a * (x + 1) + b; - double dy_l = Math.abs(yy_ref - yy_l_test); - double dy_h = Math.abs(yy_ref - yy_h_test); - if (dy_l <= dy_h) { - y--; - } - i++; - tab[i] = new Coord.Grid(x, y); - } - } + Coord.Grid[] res = new Coord.Grid[size]; + int destPos = 0; + for (int i = 0; i < tempSegments.length; i++) { + int len; + int srcPos; + if (i == 0) { + // Pierwszy segment łamanej (zawiera wszystkie kwadraty). + srcPos = 0; + len = tempSegments[i].length; } else { - // idziemy w lewo w kierunku malejacych wartosci x - if (dy > 0) { - // prosta zaierająca odcinek jest funkcją malejącą - while (x > xk) { - x--; - int yy_ref = y + 1; - double yy_l_test = a * x + b; - double yy_h_test = a * (x + 1) + b; - double dy_l = Math.abs(yy_ref - yy_l_test); - double dy_h = Math.abs(yy_ref - yy_h_test); - if (dy_l >= dy_h) { - y++; - } - i++; - tab[i] = new Coord.Grid(x, y); - } - } else { - // prosta zaierająca odcinek jest funkcją rosnącą - while (x > xk) { - x--; - int yy_ref = y; - double yy_l_test = a * x + b; - double yy_h_test = a * (x + 1) + b; - double dy_l = Math.abs(yy_ref - yy_l_test); - double dy_h = Math.abs(yy_ref - yy_h_test); - if (dy_l >= dy_h) { - y--; - } - i++; - tab[i] = new Coord.Grid(x, y); - } - } - } - } else { - // przypadek, gdy poruszamy sie po osi OY - // zamiana wspolrzednych koncow odcinka - tab = kwadratyOdcinkaDowolnego(yp, xp, yk, xk); - // zamiana wspolrzednych x <-> y - for (int i = 0; i < tab.length; i++) { - int temp = tab[i].x; - tab[i].x = tab[i].y; - tab[i].y = temp; + // Kolejny segment łamanej (bez pierwszego kwadratu). + srcPos = 1; + len = tempSegments[i].length - 1; } + System.arraycopy(tempSegments[i], srcPos, res, destPos, len); + destPos += len; } - return tab; + return res; } /** - * Funkcja pomocnicza. - * Zwraca kwadraty lezace na odcinku zorientowanaym pod dowolnym katem wzgledem osi OX. + * Funkcja wykrywa orientację wierzchołków: zgodność z ruchem wskazówek zegara. * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica kwadratow lezacych na odcinku + * @param poly + * @return */ - private static Coord.Grid[] kwadratyOdcinkaDowolnego2(int xp, int yp, int xk, int yk) { - int dx = xk - xp; - int dy = yk - yp; - if (Math.abs(dx) > Math.abs(dy)) { - // przypadek, gdy poruszamy sie po osi OX - // wyznaczenie wspolczynnikow prostej (wartosci w metrach) - float a; - float b; - float dxf = (float) dx; - float dyf = (float) dy; - a = dyf / dxf; - // wspolrzedne srodka kwadratu (xp, yp) - int xxp = xp * MapConsts.SS_SIZE + MapConsts.SS_SIZE / 2; - int yyp = yp * MapConsts.SS_SIZE + MapConsts.SS_SIZE / 2; - b = yyp - a * xxp; - - if (dx > 0) { - // idziemy w prawo w kierunku rosnacych wartosci x - Coord.Grid[] temp = new Coord.Grid[Math.abs(dx) + Math.abs(dy) + 1]; - int dl = 0; - temp[dl] = new Coord.Grid(); - temp[dl].x = xp; - temp[dl].y = yp; - dl++; - for (int x = xp + 1; x <= xk - 1; x++) { - // wspolrzedne wierzcholkow kwadratu w metrach - int xd, xg; - int yd, yg; - int y1, y2; - xd = (x - 1) * MapConsts.SS_SIZE; - xg = x * MapConsts.SS_SIZE; - float ydf = a * xd + b; - float ygf = a * xg + b; - yd = (int) ydf; - yg = (int) ygf; - y1 = yd / MapConsts.SS_SIZE + 1; - y2 = yg / MapConsts.SS_SIZE + 1; - temp[dl] = new Coord.Grid(); - temp[dl].x = x; - temp[dl].y = y1; - dl++; - if (y1 != y2) { - temp[dl] = new Coord.Grid(); - temp[dl].x = x; - temp[dl].y = y2; - dl++; - } - } - temp[dl] = new Coord.Grid(); - temp[dl].x = xk; - temp[dl].y = yk; - dl++; - Coord.Grid[] kwadraty = new Coord.Grid[dl]; - System.arraycopy(temp, 0, kwadraty, 0, dl); -// for (int i = 0; i < kwadraty.length; i++) { -// // przepisanie referencji na wspolrzedne kwadratow -// kwadraty[i] = temp[i]; -// } - return kwadraty; - } else { - // idziemy w lewo w kierunku malejacych wartosci x - // zamiana koncow odcinka - Coord.Grid[] temp = kwadratyOdcinkaDowolnego2(xk, yk, xp, yp); - Coord.Grid[] kwadraty = new Coord.Grid[temp.length]; - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = temp[kwadraty.length - 1 - i]; - } - return kwadraty; - } - } else { - // przypadek, gdy poruszamy sie po osi OY - // zamiana wspolrzednych koncow odcinka - Coord.Grid[] kwadraty = kwadratyOdcinkaDowolnego2(yp, xp, yk, xk); - // przepisanie wspolrzednych do nowej tablicy - // z zamiana wspolrzednych - for (int i = 0; i < kwadraty.length; i++) { - int temp_x = kwadraty[i].x; - kwadraty[i].x = kwadraty[i].y; - kwadraty[i].y = temp_x; - } - return kwadraty; + public static boolean isClockwise(Coord.Grid[] poly) { + if (poly.length <= 2) { + return true; } + long sum = 0; + for (int i = 0; i < poly.length; i++) { + int next = (i + 1) % poly.length; + sum += (long) (poly[next].x - poly[i].x) * (poly[next].y + poly[i].y); + } + // If sum > 0, it is clockwise. If < 0, it is counter-clockwise. + return sum >= 0; } /** - * Funkcja pomocnicza. - * Zwraca kwadraty lezace na odcinku zorientowanaym pod katem 45 lub 135 stop. wzgledem osi OX. + * Funkcja usuwa z tablicy wejściowej rejon wierzchołków obszaru powtarzające się sąsiednie wierzchołki. * - * @param xp wspolrzedna x poczatku - * @param yp wspolrzedna y poczatku - * @param xk wspolrzedna x konca - * @param yk wspolrzedna y konca - * @return tablica kwadratow lezacych na odcinku + * @param vertices tablica wierzchołków + * @return poprawioną tablicę wierzchołków */ - private static Coord.Grid[] kwadratyOdcinkaNaPrzekatnej(int xp, int yp, int xk, int yk) { - int dx = xk - xp; - int dy = yk - yp; - Coord.Grid[] kwadraty = new Coord.Grid[Math.abs(dx) + 1]; - if ((dx > 0) && (dy > 0)) { - // wektor typu "/" do gory - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp + i, yp + i); - } - } else if ((dx > 0) && (dy < 0)) { - // wektor typu "\" do dolu - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp + i, yp - i); - } - } else if ((dx < 0) && (dy < 0)) { - // wektor typu "/" do dolu - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp - i, yp - i); - } - } else { - // wektor typu "\" do gory - for (int i = 0; i < kwadraty.length; i++) { - kwadraty[i] = new Coord.Grid(xp - i, yp + i); - } - } - return kwadraty; - } - - /** - * Funkcja wyznacza ograniczenie dolne wspolrzednych rejonu - * - * @param rejon - * @return ograniczenie dolne wspolrzednych rejonu - */ - public static Coord.Grid wspXYOgranDolne(Coord.Grid[] rejon) { - if (rejon == null) { + public static Coord.Grid[] removeAdjacentDuplicates(Coord.Grid[] vertices) { + if (vertices == null) { return null; } - Coord.Grid kw_min = new Coord.Grid(); - kw_min.x = rejon[0].x; - kw_min.y = rejon[0].y; - for (int i = 1; i < rejon.length; i++) { - if (kw_min.x > rejon[i].x) { - kw_min.x = rejon[i].x; - } - if (kw_min.y > rejon[i].y) { - kw_min.y = rejon[i].y; + if (vertices.length < 2) { + return vertices; + } + int i = 0; + for (int j = i + 1; j < vertices.length; j++) { + if (!vertices[i].equals(vertices[j])) { + i++; + vertices[i] = vertices[j]; } } - return kw_min; + if (i > 0 && vertices[0].equals(vertices[i])) { + // Pominięcie ostatniego wierzchołka identycznego z pierwszym (zamykający wielokąt). + i--; + } + Coord.Grid[] newArr = new Coord.Grid[i + 1]; + System.arraycopy(vertices, 0, newArr, 0, newArr.length); + return newArr; } /** - * Funkcja wyznacza ograniczenie górne wspolrzednych rejonu + * Funkcja usuwa z listy wierzchołki pośrednie tworzące "schodek". Wykorzystywana w przypadku obiektów liniowych + * (łamanych). * - * @param rejon - * @return ograniczenie górne wspolrzednych rejonu + * @param vertices + * @return */ - public static Coord.Grid wspXYOgranGorne(Coord.Grid[] rejon) { - if (rejon == null) { - return null; + public static Coord.Grid[] removeSteps(Coord.Grid[] vertices) { + if (vertices.length < 3) { + return vertices; } - Coord.Grid kw_max = new Coord.Grid(); - kw_max.x = rejon[0].x; - kw_max.y = rejon[0].y; - for (int i = 1; i < rejon.length; i++) { - if (kw_max.x < rejon[i].x) { - kw_max.x = rejon[i].x; - } - if (kw_max.y < rejon[i].y) { - kw_max.y = rejon[i].y; - } - } - return kw_max; - } - - /** - * Funkcja bada należenie punktu do prostokąta zorientowanego równolegle do osi współrzędnych - * - * @param xMin minimalna współrzędna X prostokąta (lewego dolnego rogu) - * @param yMin minimalna współrzędna Y prostokąta (lewego dolnego rogu) - * @param xMax maksymalna współrzędna X prostokąta (prawego górnego rogu) - * @param yMax maksymalna współrzędna Y prostokąta (prawego górnego rogu) - * @param xTest współrzędna X punktu testowanego - * @param yTest współrzędna Y punktu testowanego - * @return true, jeśli testowany punkt należy do prostokąta - */ - public static boolean nalezyDoProstokata(int xMin, int yMin, int xMax, int yMax, int xTest, int yTest) { - if (xTest < xMin || xTest > xMax) { - return false; - } - if (yTest < yMin || yTest > yMax) { - return false; - } - return true; - } - - /** - * Funkcja usuwa z tablicy wejściowej rejon wierzchołków obszaru powtarzające się sąsiednie wierzchołki - * - * @param rejon tablica wierzchołków obszaru - * @return poprawioną tablicę wierzchołków obszaru - */ - public static Coord.Grid[] poprawRejon(Coord.Grid[] rejon) { - if ((rejon == null) || (rejon.length <= 0)) { - return null; - } - int j = 0; - Coord.Grid[] rejon_temp = new Coord.Grid[rejon.length]; - rejon_temp[j] = rejon[j]; - for (int i = j + 1; i < rejon.length; i++) { - if (!rejon[i].equals(rejon_temp[j])) { - if (i == rejon.length - 1) { - if (rejon[i].equals(rejon_temp[0])) { - break; - } - } - j++; - rejon_temp[j] = rejon[i]; - } - } - if ((j > 0) && (rejon_temp[0].equals(rejon_temp[j]))) { - j--; - } - Coord.Grid[] rejon_popr = new Coord.Grid[j + 1]; - System.arraycopy(rejon_temp, 0, rejon_popr, 0, rejon_popr.length); -// for (int k = 0; k < rejonPopr.length; k++) { -// rejonPopr[k] = rejon_temp[k]; -// } - return rejon_popr; - } - - /** - * Funkcja wyznacza tablice (kolekcję) kwadratow należących do zadanego obszaru - * - * @param rej kolejne wierzchołki obszaru zadawane prawoskretnie - * @return tablica kwadratów należących do zadanego obszaru - */ - public static Coord.Grid[] kwadratyObszaru(Coord.Grid[] rej) { - if (rej == null) { - return null; - } - Coord.Grid[] kwadraty = null; - Coord.Grid[] rejon = poprawRejon(rej); - if (rejon.length == 1) { //TODO Tu raczej jest blad - kwadraty = new Coord.Grid[1]; - return kwadraty; - } - if (rejon.length == 2) { - return kwadratyOdcinka(rejon[0].x, rejon[0].y, rejon[1].x, rejon[1].y); - } - Coord.Grid kw_min = wspXYOgranDolne(rejon); - Coord.Grid kw_max = wspXYOgranGorne(rejon); - int dl = (kw_max.x - kw_min.x + 1) * (kw_max.y - kw_min.y + 1); - Coord.Grid[] kwadraty_temp = new Coord.Grid[dl]; - // sprawdzenie kazdego kwadratu z duzego obszaru - // (prostokat opisany na rejonie) - dl = 0; - for (int x = kw_min.x; x <= kw_max.x; x++) { - for (int y = kw_min.y; y <= kw_max.y; y++) { - if (insidePolygon(rejon, x, y)) { -// if (nalezyDoObszaru(rejon, i, j)) { - // kwadrat o indeksach (i,j) nalezy do obszaru - kwadraty_temp[dl] = new Coord.Grid(x, y); - dl++; + boolean[] toRemove = new boolean[vertices.length]; + Coord.Grid node_i; + Coord.Grid node_ii; + Coord.Grid node_iii; + boolean stepDetected = false; + int count = 0; + for (int i = 0; i < vertices.length - 2; i++) { + node_i = vertices[i]; + node_ii = vertices[i + 1]; + node_iii = vertices[i + 2]; + int absX_i_ii = Math.abs(node_ii.x - node_i.x); + int absY_i_ii = Math.abs(node_ii.y - node_i.y); + int absX_ii_iii = Math.abs(node_iii.x - node_ii.x); + int absY_ii_iii = Math.abs(node_iii.y - node_ii.y); + if (absX_i_ii + absY_i_ii + absX_ii_iii + absY_ii_iii == 2) { + // the nodes can form a step + if (absX_i_ii + absX_ii_iii == 1) { + // the nodes form a step, so the middle node of the step should be removed + toRemove[i + 1] = true; + count++; + stepDetected = true; + // przeskok o usuwany wezel + i++; } } } - kwadraty = new Coord.Grid[dl]; - System.arraycopy(kwadraty_temp, 0, kwadraty, 0, dl); -// for (int i = 0; i < kwadraty.length; i++) { -// kwadraty[i] = new Coord.GridCoord(); -// kwadraty[i].x = kwadraty_temp[i].x; -// kwadraty[i].y = kwadraty_temp[i].y; -// } - return kwadraty; + if (stepDetected) { + Coord.Grid[] newArr = new Coord.Grid[vertices.length - count]; + int j = 0; + for (int i = 0; i < vertices.length; i++) { + if (!toRemove[i]) { + newArr[j++] = vertices[i]; + } + } + vertices = newArr; + return removeSteps(vertices); + } + return vertices; + } + + public static Coord.Grid[] removeCollinearity(Coord.Grid[] vertices, boolean isPolygon) { + if (vertices.length < 3) { + return vertices; + } + boolean[] toDelete = new boolean[vertices.length]; + Coord.Grid curr_node; + Coord.Grid next_node; + Coord.Grid prev_node; + boolean collinearFound = false; + int lenMax = vertices.length; + int iStart = 0; + if (!isPolygon) { + // łamana otwarta + lenMax--; + iStart = 1; + } + final int len = vertices.length; + int count = 0; + for (int curr = iStart; curr < lenMax; curr++) { + int next = (curr + 1) % len; + int prev = (curr + len - 1) % len; + curr_node = vertices[curr]; + next_node = vertices[next]; + prev_node = vertices[prev]; + if (GeomUtils.include(prev_node.x, prev_node.y, next_node.x, next_node.y, curr_node.x, curr_node.y)) { + // i-ty do usuniecia + toDelete[curr] = true; + collinearFound = true; + count++; + } + } + if (collinearFound) { + Coord.Grid[] newArr = new Coord.Grid[len - count]; + int j = 0; + for (int i = 0; i < len; i++) { + if (!toDelete[i]) { + newArr[j++] = vertices[i]; + } + } + vertices = newArr; + return removeCollinearity(vertices, isPolygon); + } + return vertices; + } + + /** + * Funkcja odwraca kolejność wierzchołków w tablicy. + * + * @param vertices + * @return + */ + public static Coord.Grid[] reverse(Coord.Grid[] vertices) { + if (vertices == null || vertices.length == 0) { + return vertices; + } + int mid = vertices.length / 2; + int last = vertices.length - 1; + for (int i = 0; i < mid; i++) { + vertices[i] = vertices[last - i]; + } + return vertices; } /** * Funkcja wyznacza srodek zadanego odcinka * - * @param odc odcinek wejściowy - * @return współrzędne środka zadanego odcinka */ - public static Coord.Grid srodekOdcinka(Coord.Grid[] odc) { - Coord.Grid srodek = null; - int xsr = 0, ysr = 0; - if ((odc != null) && (odc.length > 0)) { - srodek = new Coord.Grid(); - for (int j = 0; j < odc.length; j++) { - xsr = xsr + odc[j].x; - ysr = ysr + odc[j].y; - } - srodek.x = (int) xsr / odc.length; - srodek.y = (int) ysr / odc.length; - } + public static Coord.Grid srodekOdcinka(int x1, int y1, int x2, int y2) { + int xsr = (x1 + x2) / 2; + int ysr = (y1 + y2) / 2; + Coord.Grid srodek = new Coord.Grid(xsr, ysr); return srodek; } - /** - * Funkcja wyznacza srodek zadanego odcinka - * - * @param pocz początek wejściowego odcinka - * @param kon koniec wejsciowego odcinka - * @return współrzędne środka zadanego odcinka - */ - public static Coord.Grid srodekOdcinka(Coord.Grid pocz, Coord.Grid kon) { - Coord.Grid srodek = null; - int xsr = 0, ysr = 0; - if ((pocz == null) || (kon == null)) { - return srodek; - } - xsr = (pocz.x + kon.x) / 2; - ysr = (pocz.y + kon.y) / 2; - srodek = new Coord.Grid(xsr, ysr); - return srodek; - } - - /** - * Funkcja wyznacza ważony środek odcinka - * - * @param pocz początek wejściowego odcinka - * @param kon koniec wejsciowego odcinka - * @param waga waga współrzędnych początku odcinka - * @return ważone współrzędne środka zadanego odcinka - */ - public static Coord.Grid srodekOdcinkaWazony(Coord.Grid pocz, - Coord.Grid kon, float waga) { - Coord.Grid srodek = null; - if ((waga < 0.0f) || (waga > 1.0f)) { - return srodek; - } - int xsr = 0, ysr = 0; - if ((pocz == null) || (kon == null)) { - return srodek; - } - float f_xsr = pocz.x * waga + kon.x * (1.0f - waga); - float f_ysr = pocz.y * waga + kon.y * (1.0f - waga); - xsr = (int) f_xsr; - ysr = (int) f_ysr; - srodek = new Coord.Grid(xsr, ysr); - return srodek; - } - - /** - * Funkcja wyznacza pozostałą długość drogi odcinka od zadanego punktu tej drogi do jej końca - * - * @param droga zadana droga - * @param pocz punkt od którego liczona pozostała długość drogi - * @return pozostała długość odcinka od punktu pocz [m] - */ - public static float wyznaczPozostalaDlugoscDrogi(ArrayList droga, int pocz) { - if ((droga == null) || (droga.size() == 0) || (pocz < 0) || (pocz >= droga.size())) { - return 0.0f; - } - double dl = 0.0f; - Coord.Grid idkw1 = droga.get(pocz); - Coord.Grid idkw2; - for (int j = pocz + 1; j < droga.size() - 1; j++) { - idkw2 = droga.get(j); - dl += Coord.Grid.distance(idkw1.x, idkw1.y, idkw2.x, idkw2.y); - idkw1 = idkw2; - } - return (float) dl; - } - - /** - * Funkcja zwraca odleglosc miedzy srodkami malych kwadratow [m]. - * - * @param p1 współrzędna malego kwadratu pocz. - * @param p2 współrzędna malego kwadratu konc. - * @return Odległość między środkami małych kwadratów [m]. - */ - public static float odleglosc(Coord.Grid p1, Coord.Grid p2) { - // odleglosc miedzy srodkami malych kwadratow - if (null == p1 || null == p2) { - return -1.0f; - } - int xx = p1.x - p2.x; - xx *= xx; - int yy = p1.y - p2.y; - yy *= yy; - float odl = (float) Math.sqrt(xx + yy); - odl *= MapConsts.SS_SIZE; - return odl; - } - /** * Funkcja zwraca odległość między środkami małych kwadratów [m]. * @@ -1249,22 +861,87 @@ public class GeomUtils { return odl; } - /** - * Funkcja zwraca odległość między środkami małych kwadratów [m]. - * - * @param dx odległość w kwadratach na osi OX. - * @param dy odległość w kwadratach na osi OY. - * @return Odległość miedzy srodkami malych kwadratow [m]. - */ - public static float odleglosc(int dx, int dy) { - float odl = (float) Math.sqrt(dx * dx + dy * dy); - odl *= MapConsts.SS_SIZE; - return odl; + public static void print(Coord.Grid[] segment) { + StringBuilder sb = new StringBuilder(300); + sb.append('['); + int last = segment.length - 1; + for (int i = 0; i < segment.length; i++) { + sb.append('('); + sb.append(segment[i].x); + sb.append(','); + sb.append(segment[i].y); + sb.append(')'); + if (i < last) { + sb.append('|'); + } + } + sb.append(']'); + System.out.println(sb); } // ===================================================================== public static void main(String[] args) { + +// Coord.Grid[] segments = new Coord.Grid[4]; +// segments[0] = new Coord.Grid(1, 1); +// segments[1] = new Coord.Grid(6, 4); +// segments[2] = new Coord.Grid(7, 3); +// segments[3] = new Coord.Grid(8, 9); +// +// Coord.Grid[] squares = generateSquaresOfSegments(segments); +// print(squares); + + int x0 = 10; + int y0 = 10; + int x1 = 10; + int y1 = 10; + EGeoDirection d = neighborDirection(x0, y0, x1, y1); + x1 = 9; + y1 = 11; + d = neighborDirection(x0, y0, x1, y1); + x1 = 10; + y1 = 11; + d = neighborDirection(x0, y0, x1, y1); + x1 = 11; + y1 = 11; + d = neighborDirection(x0, y0, x1, y1); + x1 = 11; + y1 = 10; + d = neighborDirection(x0, y0, x1, y1); + x1 = 9; + x1 = 11; + y1 = 9; + d = neighborDirection(x0, y0, x1, y1); + x1 = 10; + y1 = 9; + d = neighborDirection(x0, y0, x1, y1); + x1 = 9; + y1 = 9; + d = neighborDirection(x0, y0, x1, y1); + x1 = 9; + y1 = 10; + d = neighborDirection(x0, y0, x1, y1); + + Coord.Grid[] segments = new Coord.Grid[12]; + segments[0] = new Coord.Grid(1, 2); + segments[1] = new Coord.Grid(2, 3); + segments[2] = new Coord.Grid(2, 4); + segments[3] = new Coord.Grid(2, 5); + segments[4] = new Coord.Grid(3, 5); + segments[5] = new Coord.Grid(4, 5); + segments[6] = new Coord.Grid(5, 6); + segments[7] = new Coord.Grid(6, 6); + segments[8] = new Coord.Grid(6, 7); + segments[9] = new Coord.Grid(7, 8); + segments[10] = new Coord.Grid(8, 8); + segments[11] = new Coord.Grid(8, 7); + + Coord.Grid[] squares = removeSteps(segments); + print(segments); + print(squares); + + double odl = Coord.Grid.distance(1, 1, 2, 2); double odl2 = Coord.Grid.distanceApprox(1, 1, 2, 2); logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl); @@ -1299,7 +976,7 @@ public class GeomUtils { for (int y = 20; y >= 0; y--) { for (int x = 0; x <= 20; x++) { stopCoord.set(x, y); - EGeoDirection kier = kierunek(startCoord, stopCoord); + EGeoDirection kier = estimateDirection(startCoord, stopCoord); switch (kier) { case NORTH: System.out.print('|'); @@ -1334,84 +1011,84 @@ public class GeomUtils { } System.out.println(); - EGeoDirection kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(10, 15)); + EGeoDirection kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(10, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(10, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(10, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 10)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 10)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 10)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 10)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(9, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(9, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(11, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(11, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(9, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(9, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(11, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(11, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 9)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 9)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 11)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 11)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 9)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 9)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 11)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 11)); System.out.println(kier); // Kierunki pośrednie: PLN-WSC, PLD-WSC, PLN-ZAC, PLD-ZAC - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 5)); System.out.println(kier); // PLN-WSC - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 16)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 16)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 14)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 14)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(16, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(16, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(14, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(14, 15)); System.out.println(kier); // PLD-WSC - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 6)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 6)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(15, 4)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 4)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(16, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(16, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(14, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(14, 5)); System.out.println(kier); //PLN-ZAC - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 16)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 16)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 14)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 14)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(6, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(6, 15)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(4, 15)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(4, 15)); System.out.println(kier); // PLD-ZAC - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 6)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 6)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(5, 4)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 4)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(6, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(6, 5)); System.out.println(kier); - kier = kierunek(new Coord.Grid(10, 10), new Coord.Grid(4, 5)); + kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(4, 5)); System.out.println(kier); boolean przeciecie = intersection(p, q, r, s); @@ -1436,7 +1113,6 @@ public class GeomUtils { Coord.Grid[] lamana = {new Coord.Grid(8, 8), new Coord.Grid(3, 4), new Coord.Grid(6, 11)}; - Coord.Grid[] tab = kwadratyLamanej2(lamana); long det = det3(1, 0, 4, 0, 2, 2); diff --git a/src/main/java/pl/wat/ms4ds/terrain/RightBigSquare.java b/src/main/java/pl/wat/ms4ds/terrain/RightBigSquare.java index 70acdaf..3c2d3c2 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/RightBigSquare.java +++ b/src/main/java/pl/wat/ms4ds/terrain/RightBigSquare.java @@ -2,7 +2,7 @@ package pl.wat.ms4ds.terrain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pl.wat.ms4ds.terrain.nmt.NMTDataReader; +import pl.wat.ms4ds.terrain.nmt.NmtDataGenerator; import java.io.*; @@ -33,7 +33,7 @@ public class RightBigSquare extends BigSquare { fn = MapConsts.DATA_DIR + fileName + ".bin"; } BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fn)); - byte[] buf = new byte[9 * 256]; + byte[] buf = new byte[10 * 256]; int offset = 0; for (int x = 0; x < squares.length; x++) { for (int y = 0; y < squares[0].length; y++) { @@ -59,7 +59,7 @@ public class RightBigSquare extends BigSquare { try { String fullPath = dir + fileName + ".bin"; BufferedInputStream in = new BufferedInputStream(new FileInputStream(fullPath), 2 * 8192); - byte[] buffer = new byte[9 * 512]; + byte[] buffer = new byte[10 * 512]; int offset = 0; int count = in.read(buffer); squares = new Square[MapConsts.SS_PER_BS_X][MapConsts.SS_PER_BS_Y]; @@ -71,17 +71,11 @@ public class RightBigSquare extends BigSquare { offset = 0; } offset = kw.readFromBuffer(buffer, offset); -// if (kw.elevation < NMTDataReader.H_MIN) { -// logger.warn("Elevation: {}, fn= {} ", kw.elevation, fileName); -// } -// if (kw.elevation > NMTDataReader.H_MAX) { -// logger.warn("Elevation: {}, fn= {} ", kw.elevation, fileName); -// } squares[x][y] = kw; } } in.close(); - logger.debug("Doczytano plik mapy: " + fullPath); + logger.debug("Doczytano plik mapy: {}", fullPath); } catch (IOException e) { squares = null; throw e; @@ -126,17 +120,17 @@ public class RightBigSquare extends BigSquare { directory.mkdirs(); sb.append(fileName + ".bin"); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(sb.toString())); - byte[] buf = new byte[9 * 256]; + byte[] buf = new byte[10 * 256]; int offset = 0; for (int x = 0; x < squares.length; x++) { for (int i = 0; i < m; i++) { for (int y = 0; y < squares[0].length; y++) { Square square = squares[x][y]; - if (square.elevation > NMTDataReader.H_MAX) { + if (square.elevation > NmtDataGenerator.H_MAX) { logger.warn("Elevation: {}, fn= {}", square.elevation, fileName); } for (int j = 0; j < m; j++) { - offset = square.writeToBufferElevationOnly(buf, offset); + offset = square.writeToBuffer_ElevationOnly(buf, offset); if (offset >= buf.length) { out.write(buf); offset = 0; @@ -174,16 +168,16 @@ public class RightBigSquare extends BigSquare { // case 0, 1, 4: // hex = 0; // break; - case 2: + case SWAMP: hex_s = 100; break; - case 3: + case WATER: hex_w = 100; break; - case 5: + case BUILDINGS: hex_b = 100; break; - case 6: + case FOREST: hex_f = 100; break; default: @@ -259,31 +253,32 @@ public class RightBigSquare extends BigSquare { for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { Square kw = new Square(x, y); squares[x][y] = kw; - kw.terrainType = (short) TerrainType.BARE_GROUND.ID; + kw.terrainType = TerrainType.NONE; int hex = in.readByte(); if (hex > 30) { - kw.terrainType = (short) TerrainType.FOREST.ID; + kw.terrainType = TerrainType.FOREST; } hex = in.readByte(); if (hex > 30) { - kw.terrainType = (short) TerrainType.WATER.ID; + kw.terrainType = TerrainType.WATER; } hex = in.readByte(); if (hex > 30) { - kw.terrainType = (short) TerrainType.BUILDINGS.ID; + kw.terrainType = TerrainType.BUILDINGS; } hex = in.readByte(); if (hex > 30) { - kw.terrainType = (short) TerrainType.SWAMP.ID; + kw.terrainType = TerrainType.SWAMP; } kw.elevation = in.readInt(); - if (kw.elevation < NMTDataReader.H_MIN) { + if (kw.elevation < NmtDataGenerator.H_MIN) { logger.warn("Elevation: {}, fn= {}", kw.elevation, fileName); } - if (kw.elevation > NMTDataReader.H_MAX) { + if (kw.elevation > NmtDataGenerator.H_MAX) { logger.warn("Elevation: {}, fn= {}", kw.elevation, fileName); } - kw.roznicaWzniesien = in.readInt(); + // Pomijam dane o różnicy wzniesień. + in.readInt(); int bit_1 = 1; hex = in.readByte(); for (int i = 0; i < 8; i++) { diff --git a/src/main/java/pl/wat/ms4ds/terrain/Square.java b/src/main/java/pl/wat/ms4ds/terrain/Square.java index a4da211..8b9d562 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/Square.java +++ b/src/main/java/pl/wat/ms4ds/terrain/Square.java @@ -1,10 +1,21 @@ package pl.wat.ms4ds.terrain; - -import pl.wat.ms4ds.terrain.nmt.NMTDataReader; - +/** + * + * A class representing the characteristics of a square of terrain within a regular grid. + * + */ public class Square { + /** + * Horizontal grid coordinate. + */ + public final int x; + /** + * Vertical grid coordinate. + */ + public final int y; + /** * The height above the level of the sea. Unit of measure meter [m]. */ @@ -20,7 +31,7 @@ public class Square { * 5 - BUILDINGS * 6 - FOREST */ - public short terrainType; + public TerrainType terrainType; /** * Type of watercourse (water obstacle) in a given direction. Each index corresponds to a given direction. @@ -34,18 +45,38 @@ public class Square { */ public final byte[] roads; - /// ///////////////////////////////////// - /// tymczasowo - public float stopienZabudowy; - public float stopienZalesienia; - public float stopienZawodnienia; - public float stopienZabagnienia; - public boolean[] jestDroga; - public boolean[] jestRow; - public boolean[] jestPrzeszkodaWodna; - public int roznicaWzniesien; - public int wysokoscSrednia; + /** + * A type of crossing that allows overcoming terrain obstacles, e.g. rivers. + *

Possible values: 0 - none, 1 - bridge, 2 - tunnel + */ + public CrossingType crossingType; + /** + * Size of the square. + */ + public static final int SIZE = 50; + + public enum CrossingType { + NONE(0), + BRIDGE(1), + TUNNEL(2); + + static final CrossingType[] values = values(); + + public static CrossingType valueById(int id) { + return (0 <= id && id <= 2) ? values[id] : NONE; + } + + public final int id; + + CrossingType(int id) { + this.id = id; + } + } + + /** + * Obiekt reprezentujący tzw. pusty kwadrat (spoza obszaru). + */ public static final Square EMPTY = new Square(-1, -1); public Square() { @@ -59,37 +90,12 @@ public class Square { watercourses = new byte[8]; } - public Square(int x, int y, short elevation, byte terrainType, byte majorRoads, byte minorRoads, byte smallRoads, byte rivers, byte streams, byte drains) { - this.x = x; - this.y = y; - roads = new byte[8]; - watercourses = new byte[8]; - // Konwersja na metry a[0.25m] -> b[m] - this.elevation = (float) (elevation) / 4; - this.terrainType = terrainType; - int bit = 1; - for (int i = 0; i < 8; i++) { - int b1 = ((majorRoads & bit) > 0) ? 3 : 0; - int b2 = ((minorRoads & bit) > 0) ? 2 : 0; - int b3 = ((smallRoads & bit) > 0) ? 1 : 0; - roads[i] = (byte) (b1 + b2 + b3); - b1 = ((rivers & bit) > 0) ? 3 : 0; - b2 = ((streams & bit) > 0) ? 2 : 0; - b3 = ((drains & bit) > 0) ? 1 : 0; - watercourses[i] = (byte) (b1 + b2 + b3); - bit <<= 1; - } - } - - public int writeToBufferElevationOnly(byte[] buffer, int offset) { + public int writeToBuffer_ElevationOnly(byte[] buffer, int offset) { // Konwersja [m] -> [0.25m]. int elev = (short) (elevation * 4); byte b1 = (byte) (elev & 0xFF); elev >>= 8; byte b0 = (byte) (elev & 0xFF); - if (b0 == -1 && b1 == -4) { - System.out.println("a"); - } buffer[offset] = b0; buffer[offset + 1] = b1; buffer[offset + 2] = 0; @@ -99,7 +105,8 @@ public class Square { buffer[offset + 6] = 0; buffer[offset + 7] = 0; buffer[offset + 8] = 0; - return offset + 9; + buffer[offset + 9] = 0; + return offset + 10; } public int writeToBuffer(byte[] buffer, int offset) { @@ -112,7 +119,7 @@ public class Square { byte smallRoads = 0; byte minorRoads = 0; byte majorRoads = 0; - for (int i = 0; i < watercourses.length; i++) { + for (int i = 0; i < 8; i++) { switch (watercourses[i]) { case 1: drains |= bit; @@ -141,69 +148,64 @@ public class Square { } bit <<= 1; } + // Konwersja short -> 2 bytes byte b1 = (byte) (elev & 0xFF); elev >>= 8; byte b0 = (byte) (elev & 0xFF); - if (b0 == -1 && b1 == -4) { - System.out.println("a"); - } buffer[offset + 1] = b1; buffer[offset] = b0; - buffer[offset + 2] = (byte) terrainType; + buffer[offset + 2] = (byte) terrainType.id; buffer[offset + 3] = smallRoads; buffer[offset + 4] = minorRoads; buffer[offset + 5] = majorRoads; buffer[offset + 6] = drains; buffer[offset + 7] = streams; buffer[offset + 8] = rivers; - return offset + 9; + // 0 - brak, 1 - most, 2 - tunel + buffer[offset + 9] = (byte) crossingType.id; + return offset + 10; } public int readFromBuffer(byte[] buffer, int offset) { + // Odczyt wartości typu short + // int elev = buffer[offset] & 0xFF; // elev = (elev << 8) + (buffer[offset + 1] & 0xFF); elev = (elev << 8) | (buffer[offset + 1] & 0xFF); // Rzutowanie "elev" na short zachowuje znak liczby. short v = (short) elev; + // Konwersja jednostek [0.25m]->[m] elevation = (float) (v) / 4; - - // Konwersja na metry a[0.25m] -> b[m] -// elevation = (float) ((short) elev) / 4; - - if (elevation > NMTDataReader.H_MAX) { - System.out.println("h=" + elevation); - } - terrainType = buffer[offset + 2]; + terrainType = TerrainType.valueFromId(buffer[offset + 2]); byte smallRoads = buffer[offset + 3]; byte minorRoads = buffer[offset + 4]; byte majorRoads = buffer[offset + 5]; byte drains = buffer[offset + 6]; byte streams = buffer[offset + 7]; byte rivers = buffer[offset + 8]; + byte b = buffer[offset + 9]; + crossingType = CrossingType.valueById(b); int bit = 1; // 8 kierunków geograficznych (0 - NORTH, 1 - NORTH_EAST, ...) for (int i = 0; i < 8; i++) { - int b1 = ((majorRoads & bit) > 0) ? 3 : 0; - int b2 = ((minorRoads & bit) > 0) ? 2 : 0; - int b3 = ((smallRoads & bit) > 0) ? 1 : 0; + int b1 = ((majorRoads & bit) != 0) ? 3 : 0; + int b2 = ((minorRoads & bit) != 0) ? 2 : 0; + int b3 = ((smallRoads & bit) != 0) ? 1 : 0; roads[i] = (byte) (b1 + b2 + b3); - b1 = ((rivers & bit) > 0) ? 3 : 0; - b2 = ((streams & bit) > 0) ? 2 : 0; - b3 = ((drains & bit) > 0) ? 1 : 0; + b1 = ((rivers & bit) != 0) ? 3 : 0; + b2 = ((streams & bit) != 0) ? 2 : 0; + b3 = ((drains & bit) != 0) ? 1 : 0; watercourses[i] = (byte) (b1 + b2 + b3); bit <<= 1; } - return offset + 9; + return offset + 10; } - public final int x; - public final int y; - @Override public final boolean equals(Object o) { - if (!(o instanceof Square SQUARE)) return false; + if (!(o instanceof Square square)) return false; - return x == SQUARE.x && y == SQUARE.y; + return x == square.x && y == square.y; } @Override @@ -219,13 +221,13 @@ public class Square { StringBuilder linia = new StringBuilder(100); linia.append("["); char c = switch (terrainType) { - case 1 -> 'G'; - case 2 -> 'S'; - case 3 -> 'W'; - case 4 -> 'R'; - case 5 -> 'B'; - case 6 -> 'F'; - default -> ' '; + case NONE -> 'G'; + case GRASS -> 'g'; + case SWAMP -> 'S'; + case WATER -> 'W'; + case SCRUB_BUSHES -> 'R'; + case BUILDINGS -> 'B'; + case FOREST -> 'F'; }; linia.append(c); linia.append(' '); diff --git a/src/main/java/pl/wat/ms4ds/terrain/Teren.java b/src/main/java/pl/wat/ms4ds/terrain/Teren.java index cda8bcf..295e38e 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/Teren.java +++ b/src/main/java/pl/wat/ms4ds/terrain/Teren.java @@ -1,7 +1,7 @@ package pl.wat.ms4ds.terrain; import java.io.*; -import java.util.HashSet; +import java.util.ArrayList; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; @@ -9,7 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.wat.ms4ds.common.ERodzajPodwozia; import pl.wat.ms4ds.common.ERodzajTerenuPokrycie; -import pl.wat.ms4ds.terrain.nmt.NMTDataProvider; +import pl.wat.ms4ds.terrain.nmt.NmtDataProvider; import static pl.wat.ms4ds.terrain.Square.EMPTY; @@ -51,13 +51,13 @@ public class Teren { if (bs instanceof RightBigSquare rbs) { try { rbs.writeToFile(dir); - bigSquares[i][j] = null; } catch (IOException e) { LOGGER.warn("Błąd zapisu pliku mapy: " + rbs.fileName); } } } } + reset(); } public static void reset() { @@ -66,10 +66,7 @@ public class Teren { bigSquares[i][j] = null; } } - bigSquaresInMemory = 0; - for (int i = 0; i < history.length; i++) { - history[i].set(-1, -1); - } + cache.clear(); System.gc(); } @@ -81,7 +78,7 @@ public class Teren { * @return Nazwa zwracanego pliku z danymi (null - gdy niepoprawne współrzędne). */ public static String getFileName(double lat, double lon) { - int idX = Coord.convertLonTOGridX(lon); + int idX = Coord.convertLonToGridX(lon); int idY = Coord.convertLatToGridY(lat); int bigX = idX / MapConsts.SS_PER_BS_X; int bigY = idY / MapConsts.SS_PER_BS_Y; @@ -159,7 +156,7 @@ public class Teren { } public static Square getSquare(double lat, double lon) { - int idX = Coord.convertLonTOGridX(lon); + int idX = Coord.convertLonToGridX(lon); int idY = Coord.convertLatToGridY(lat); return getSquare(idX, idY); } @@ -196,34 +193,21 @@ public class Teren { } private static Coord.Grid[] history = new Coord.Grid[MapConsts.MAX_BIG_SQUARES_IN_MEMORY]; - private static int bigSquaresInMemory = 0; + private static final ArrayList cache = new ArrayList<>(MapConsts.MAX_BIG_SQUARES_IN_MEMORY); private static void makeRoom(int bigX, int bigY) { - if (bigSquaresInMemory >= MapConsts.MAX_BIG_SQUARES_IN_MEMORY) { - // najpierw zapisuję w pliku dokonane zmiany - if (bigSquares[history[0].x][history[0].y] instanceof RightBigSquare rbs) { - try { - //!! dla potrzeb generowania danych - rbs.liczbaZmian = 1; - rbs.writeToFile(null); - } catch (IOException e) { - e.printStackTrace(); - } + if (cache.size() >= MapConsts.MAX_BIG_SQUARES_IN_MEMORY) { + // Brak miejsca, zatem zwalniam/usuwam najstarszy element; + Coord.Grid removing = cache.removeFirst(); + RightBigSquare rbs = (RightBigSquare) bigSquares[removing.x][removing.y]; + try { + rbs.writeToFile(null); + } catch (IOException _) { } - // następnie usuwam duzy kwadrat z pamięci - bigSquares[history[0].x][history[0].y] = null; - for (int i = 1; i < history.length; i++) { - // przesuwam elementy w lewo - history[i - 1].set(history[i]); - } - // uaktualniam indeksy duzego kwadratu w historii (ostatni == najnowszy element) - history[MapConsts.MAX_BIG_SQUARES_IN_MEMORY - 1].x = bigX; - history[MapConsts.MAX_BIG_SQUARES_IN_MEMORY - 1].y = bigY; - } else { - history[bigSquaresInMemory].x = bigX; - history[bigSquaresInMemory].y = bigY; - bigSquaresInMemory++; + bigSquares[removing.x][removing.y] = null; + LOGGER.debug("Big square X= {}, Y= {}, fn= {} removed from cache", bigX, bigY, rbs.fileName); } + cache.add(new Coord.Grid(bigX, bigY)); } /** @@ -398,15 +382,16 @@ public class Teren { LOGGER.debug("start"); // Teren.normalizujDanePokrycia(); - String newDir = "D:/work/kwadraty_nmt/temp/25/"; + String newDir = "D:/work/kwadraty_nmt/temp/100m/"; String dir1 = "D:/work/terrain/"; String inDir = "D:/work/kwadraty_nmt/withElevation/25m/"; String dir2 = "C:/Workspace/_data/swdt/ms4ds/teren/kwadraty/100m/"; - Set fileNames = NMTDataProvider.listFiles(dir2); - Set fileNames2 = new HashSet<>(); - fileNames2.add(dir2 + "E014C_N53D.bin"); - Teren.generateDataFromOldFormat(fileNames, dir1, 25); + Set fileNames = NmtDataProvider.listFiles(inDir); + generateDataFromOldFormat(fileNames, newDir, 100); +// Set fileNames2 = new HashSet<>(); +// fileNames2.add(dir2 + "E014C_N53D.bin"); +// generateData(fileNames, inDir, newDir); // Square kw = getKwadrat(1500, 2100); // System.out.println(kw); @@ -454,7 +439,6 @@ public class Teren { rbs.readFromFile_OldFormat(null); rbs.writeToFile_ElevationOnly(outDir, dlmk); } - } /** @@ -471,7 +455,17 @@ public class Teren { rbs.readFromFile(inDir); rbs.writeToFile_OldFormat(outDir); } + } + public static void generateData(Set fileNames, String inDir, String outDir) throws IOException { + for (String fileName : fileNames) { + File file = new File(fileName); + String fn = file.getName().substring(0, file.getName().lastIndexOf('.')); + RightBigSquare rbs = new RightBigSquare(); + rbs.fileName = fn; + rbs.readFromFile(inDir); + rbs.writeToFile(outDir); + } } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/TerrainType.java b/src/main/java/pl/wat/ms4ds/terrain/TerrainType.java index 765255d..f893fdf 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/TerrainType.java +++ b/src/main/java/pl/wat/ms4ds/terrain/TerrainType.java @@ -1,7 +1,7 @@ package pl.wat.ms4ds.terrain; public enum TerrainType { - BARE_GROUND(0), + NONE(0), GRASS(1), SWAMP(2), WATER(3), @@ -9,30 +9,16 @@ public enum TerrainType { BUILDINGS(5), FOREST(6); - static { - BARE_GROUND.height = 0; - BARE_GROUND.passability = true; + static final TerrainType[] values = values(); + public static TerrainType valueFromId(int id) { + return values[id]; } - public int getHeight(byte terrainType) { - return height; - } - - public final int ID; - private int height; - /** - * Zdolność przekraczania - */ - private boolean passability; + public final int id; TerrainType(int id) { - this.ID = id; + this.id = id; } - TerrainType(int id, int height, boolean passability) { - this.ID = id; - this.height = height; - this.passability = passability; - } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/TerrainUtils.java b/src/main/java/pl/wat/ms4ds/terrain/TerrainUtils.java index 5ad3e44..0ec2a8b 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/TerrainUtils.java +++ b/src/main/java/pl/wat/ms4ds/terrain/TerrainUtils.java @@ -18,88 +18,70 @@ public class TerrainUtils { * @param y2 * @return */ - public static float widocznoscOptyczna(float ho, float ht, int x1, int y1, int x2, int y2) { + public static int lineOfSight(float ho, float ht, int x1, int y1, int x2, int y2) { if ((x1 == x2) && (y1 == y2)) { - return 1.0f; + return 1; } - Square kwDo = Teren.getSquare(x1, y1); - Square kwOd = Teren.getSquare(x2, y2); - if (kwDo == Square.EMPTY || kwOd == Square.EMPTY) { - return 0.0f; + Square start = Teren.getSquare(x1, y1); + Square stop = Teren.getSquare(x2, y2); + if (start == Square.EMPTY || stop == Square.EMPTY) { + return 0; } // roznica wysokosci miedzy skrajnymi kwadratami - float roznicaWysokosci = kwDo.wysokoscSrednia + ht - kwOd.wysokoscSrednia - ho; - float wysBezwzgObserwatora; - if (roznicaWysokosci < 0) { - // sprawdzanie kwOd -> kwDo + float heightDiff; + float observerTotalHeight = start.elevation + ho; + float targetTotalHeight = stop.elevation + ht; + if (observerTotalHeight > targetTotalHeight) { + // zamiana ról int swap = x1; x1 = x2; x2 = swap; swap = y1; y1 = y2; y2 = swap; - roznicaWysokosci = -roznicaWysokosci; - wysBezwzgObserwatora = kwDo.wysokoscSrednia + ht; + heightDiff = observerTotalHeight - targetTotalHeight; + observerTotalHeight = targetTotalHeight; } else { - wysBezwzgObserwatora = kwOd.wysokoscSrednia + ho; + heightDiff = targetTotalHeight - observerTotalHeight; } - Coord.Grid[] kwadratyNaOdcinku = Bresenham.generateSegment(x1, y1, x2, y2); - double dlugoscOdcinka = Coord.Grid.distance(x1, y1, x2, y2); - double tangAlfa0 = roznicaWysokosci / dlugoscOdcinka; + int[] seq = GeomUtils.generateSquaresOfSegment2(x1, y1, x2, y2); + double dist = Coord.Grid.distance(x1, y1, x2, y2); + double tangAlfa0 = heightDiff / dist; float dh_max = 0; - for (int i = 1; i < kwadratyNaOdcinku.length - 1; i++) { + // Tablica współrzędnych: x0, y0, x1, y1, x2, y2, x3, y3... + // Testowane kwadraty pośrednie między startowym a końcowym. + for (int i = 2; i < seq.length - 2; ) { // badanie wewnetrznych kwadratow nalezacych do odcinka, // czy nie sa powyzej linii widocznosci dla kwadratow skrajnych - float wysokoscPrzeszkody = 0.0f; - Square kwAkt = Teren.getSquare(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y); - if (kwAkt.stopienZalesienia > 0.5f) { - wysokoscPrzeszkody = 10.0f; - } - if (kwAkt.stopienZabudowy > 0.5f) { - wysokoscPrzeszkody = 10.0f; - } + int x = seq[i++]; + int y = seq[i++]; + Square curr = Teren.getSquare(x, y); + float obstacleHeight = switch (curr.terrainType) { + case TerrainType.SCRUB_BUSHES -> 1; + case TerrainType.BUILDINGS -> 10; + case TerrainType.FOREST -> 10; + default -> 0; + }; // wyznaczenie roznicy wysokosci kwadratu badanego i docelowego // uwzgledniajac wysokosc obserwatora oraz wysokosc przeszkody - float dh = kwAkt.wysokoscSrednia + wysokoscPrzeszkody - wysBezwzgObserwatora; + float dh = curr.elevation + obstacleHeight - observerTotalHeight; if (dh_max >= dh) { continue; } - - double odleg = Coord.Grid.distance(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y, x1, y1); -// float tangAlfa = roznWysAkt / odleg; -// if (tangAlfa0 < tangAlfa) { - if (tangAlfa0 * odleg < dh) { - // wysokosc aktualnie badanego kwadratu jest powyzej/ponizej + dist = Coord.Grid.distance(x, y, x1, y1); + // float tangAlfa = roznWysAkt / odleg; + // if (tangAlfa0 < tangAlfa) { + if (tangAlfa0 * dist < dh) { + // wysokosc aktualnie badanego kwadratu jest powyzej // linii poprowadzonej z kwadratu startowego do docelowego (z uwzglednieniem wysokosci obserwatora i celu) - // odpowiednio dla katow dodatnich/ujemnych + // odpowiednio dla katow dodatnich - return 0.0f; + return 0; } dh_max = dh; } - return 1.0f; - } - - public static float widocznoscOptyczna(int x, int y) { - Square kw = Teren.getSquare(x, y); - if (kw.stopienZabudowy > 0.25f || kw.stopienZalesienia > 0.25f) { - return 0.3f; - } - return 1.0f; - } - - public static float sredStopienWidoczOptycznej(float wysokoscObserwatora, float wysokoscCelu, - Coord.Grid kwadratOd, int dl1, Coord.Grid kwadratDo, int dl2) { - float stop = 0.0f; - for (int x1 = kwadratOd.x; x1 < kwadratOd.x + dl1; x1++) - for (int y1 = kwadratOd.y; y1 < kwadratOd.y + dl1; y1++) - for (int x2 = kwadratDo.x; x2 < kwadratDo.x + dl2; x2++) - for (int y2 = kwadratDo.y; y2 < kwadratDo.y + dl2; y2++) - stop += widocznoscOptyczna(wysokoscObserwatora, wysokoscCelu, x1, y1, x2, y2); - - stop /= (dl1 * dl1 * dl2 * dl2); - return stop; + return 1; } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTData.java b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtData.java similarity index 95% rename from src/main/java/pl/wat/ms4ds/terrain/nmt/NMTData.java rename to src/main/java/pl/wat/ms4ds/terrain/nmt/NmtData.java index 3acba04..f8b2c77 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTData.java +++ b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtData.java @@ -6,7 +6,7 @@ package pl.wat.ms4ds.terrain.nmt; * Cachuje i agreguje dane dotyczące wysokości odczytane z siatki punktów * w ramach wyznaczonych granic kwadratu terenu (współrzędne PUWG1992). */ -public class NMTData { +public class NmtData { /** * Współrzędna X kwadratu w ramach siatki terenu. @@ -48,7 +48,7 @@ public class NMTData { public double nur; - public NMTData(int x, int y, double sum, int count) { + public NmtData(int x, int y, double sum, int count) { this.x = x; this.y = y; this.sum = sum; diff --git a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataReader.java b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataGenerator.java similarity index 93% rename from src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataReader.java rename to src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataGenerator.java index ab98156..8e1ad76 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataReader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataGenerator.java @@ -10,9 +10,13 @@ import java.util.concurrent.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -public class NMTDataReader { +/** + * Klasa odpowiedzialna za generowanie danych wysokościowych w przyjętym formacie (sieci kwadratów) na podstawie + * pobranych danych NMT (spakowane w plikach o nazwach skorowidzów). + */ +public class NmtDataGenerator { - private static final Logger logger = LoggerFactory.getLogger(NMTDataReader.class); + private static final Logger logger = LoggerFactory.getLogger(NmtDataGenerator.class); static void main(String[] args) { @@ -38,7 +42,7 @@ public class NMTDataReader { String inDir = "D:/work/nmt/"; String workDir = "D:/work/temp/"; - String outDir = "D:/work/kwadraty_nmt/withElevation/50m/"; + String outDir = "D:/work/kwadraty_nmt/temp/100m/"; ArrayList list = new ArrayList<>(); list.add("m-33"); @@ -58,52 +62,6 @@ public class NMTDataReader { } - /** - * Generuje dane na podstawie rozpakowanych danych z podanego katalogu. - * - * @param inDir - * @param workDir - * @param outDir - */ - static void generateNMTData(String inDir, String workDir, String outDir) { - Set files = NMTDataProvider.listFiles(inDir); - HashMap nmtDataHashMap = new HashMap<>(); - for (String fn : files) { - String fpath = workDir + fn; - try { - readFromFile(fpath, nmtDataHashMap); - File f = new File(fpath); - f.delete(); - } catch (Exception e) { - logger.warn("Error while reading from file: {}", fpath); - try (BufferedWriter writer = new BufferedWriter(new FileWriter("D:/Work/nmt/status.txt", true))) { - writer.write("Error while processing file: " + fn); - } catch (IOException e1) { - logger.error(e1.getMessage()); - } - } - } - for (Coord.Grid gridCoord : nmtDataHashMap.keySet()) { - NMTData nmtData = nmtDataHashMap.get(gridCoord); - Square square = Teren.getSquare(gridCoord.x, gridCoord.y); - if (square == Square.EMPTY) { - continue; - } - if (nmtData.count > 0) { - square.elevation = (float) (nmtData.sum / nmtData.count); - // Konwersja na jednostkę 0.25m i zaokrąglenie do (0.0, 0.25, 0.5, 0.75) - square.elevation *= 4; - square.elevation = (int) square.elevation; - square.elevation /= 4; - if (H_MIN >= square.elevation || H_MAX <= square.elevation) { - logger.trace("!!!Dane poza zakresem: h= {}", square.elevation); - } - } - } - nmtDataHashMap.clear(); - Teren.saveToFiles(outDir); - } - /** * Generuje dane na podstawie listy spakowanych (zip) plików z podanego katalogu. * @@ -115,6 +73,7 @@ public class NMTDataReader { * @param outDir */ static void generateNMTData(ExecutorService executor, String fn_list, int startPos, int endPos, String inDir, String workDir, String outDir) { + final int TN = 8; File file = new File(fn_list); ArrayList fileNames = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) { @@ -133,7 +92,7 @@ public class NMTDataReader { break; } int j; - for (j = 0; j < 8; j++) { + for (j = 0; j < TN; j++) { if (i >= fileNames.size()) { break; } @@ -152,7 +111,7 @@ public class NMTDataReader { } catch (Exception e) { logger.warn(e.getMessage()); } - HashMap nmtDataHashMap = new HashMap<>(); + HashMap nmtDataHashMap = new HashMap<>(); for (String ufn : unzippedFileNames) { String fpath = workDir + ufn; try { @@ -171,7 +130,7 @@ public class NMTDataReader { } } for (Coord.Grid gridCoord : nmtDataHashMap.keySet()) { - NMTData nmtData = nmtDataHashMap.get(gridCoord); + NmtData nmtData = nmtDataHashMap.get(gridCoord); if (nmtData.count > 0) { Square square = Teren.getSquare(gridCoord.x, gridCoord.y); if (square == Square.EMPTY) { @@ -201,8 +160,10 @@ public class NMTDataReader { try { future.get(); } catch (InterruptedException | ExecutionException e) { + logger.warn("Error in thread: {}", e.getMessage()); } } + futures.clear(); if (count % 2000 == 0) { Teren.saveToFiles(outDir); } @@ -211,6 +172,52 @@ public class NMTDataReader { logger.info("Finished processing file list: {}", fn_list); } + /** + * Generuje dane na podstawie rozpakowanych danych z podanego katalogu. + * + * @param inDir + * @param workDir + * @param outDir + */ + static void generateNMTData(String inDir, String workDir, String outDir) { + Set files = NmtDataProvider.listFiles(inDir); + HashMap nmtDataHashMap = new HashMap<>(); + for (String fn : files) { + String fpath = workDir + fn; + try { + readFromFile(fpath, nmtDataHashMap); + File f = new File(fpath); + f.delete(); + } catch (Exception e) { + logger.warn("Error while reading from file: {}", fpath); + try (BufferedWriter writer = new BufferedWriter(new FileWriter("D:/Work/nmt/status.txt", true))) { + writer.write("Error while processing file: " + fn); + } catch (IOException e1) { + logger.error(e1.getMessage()); + } + } + } + for (Coord.Grid gridCoord : nmtDataHashMap.keySet()) { + NmtData nmtData = nmtDataHashMap.get(gridCoord); + Square square = Teren.getSquare(gridCoord.x, gridCoord.y); + if (square == Square.EMPTY) { + continue; + } + if (nmtData.count > 0) { + square.elevation = (float) (nmtData.sum / nmtData.count); + // Konwersja na jednostkę 0.25m i zaokrąglenie do (0.0, 0.25, 0.5, 0.75) + square.elevation *= 4; + square.elevation = (int) square.elevation; + square.elevation /= 4; + if (H_MIN >= square.elevation || H_MAX <= square.elevation) { + logger.trace("!!!Dane poza zakresem: h= {}", square.elevation); + } + } + } + nmtDataHashMap.clear(); + Teren.saveToFiles(outDir); + } + private static void saveFileList(String fn, String[] fileList) throws IOException { BufferedWriter writer = new BufferedWriter(new FileWriter(fn)); for (String val : fileList) { @@ -222,7 +229,7 @@ public class NMTDataReader { // usuwanie z nazwy nadmiarowych rozszerz przed rozszerzeniem static void renameFiles(String inDir, String outDir) { - Set fileNames = NMTDataProvider.listFiles(inDir); + Set fileNames = NmtDataProvider.listFiles(inDir); for (String fn : fileNames) { String nfn = fn.substring(0, fn.indexOf('.')); File fileToMove = new File(inDir + fn); @@ -236,7 +243,7 @@ public class NMTDataReader { // dodanie 0 do liczb dla wyrównania długości static void renameFiles2(String inDir, String outDir) { - Set fileNames = NMTDataProvider.listFiles(inDir); + Set fileNames = NmtDataProvider.listFiles(inDir); for (String fn : fileNames) { if (fn.length() >= 20) { continue; @@ -267,7 +274,7 @@ public class NMTDataReader { } } - private static void readFromFile(String fn, HashMap nmtDataHashMap) throws IOException { + private static void readFromFile(String fn, HashMap nmtDataHashMap) throws IOException { File file = new File(fn); InputStream inputStream = new FileInputStream(file); try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { @@ -290,7 +297,7 @@ public class NMTDataReader { public static final int H_MIN = -70; public static final int H_MAX = 2660; - private static void readASC(BufferedReader br, String firstLine, HashMap nmtDataHashMap) throws IOException { + private static void readASC(BufferedReader br, String firstLine, HashMap nmtDataHashMap) throws IOException { String line = firstLine; // Dzieli na podstringi biorąc jako znak podziału spację i jej wielokrotność // nawias kwadratowy zawiera znaki podziału @@ -336,7 +343,7 @@ public class NMTDataReader { s = line.substring(start); data[i][ncols - 1] = Double.parseDouble(s); } - NMTData nmtData = new NMTData(-1, -1, 0, 0); + NmtData nmtData = new NmtData(-1, -1, 0, 0); Coord.Geo geoCoord = new Coord.Geo(); Coord.Puwg puwgCoord = new Coord.Puwg(); double h; @@ -355,11 +362,11 @@ public class NMTDataReader { if (nmtData.ell > x_puwg || nmtData.eur < x_puwg || nmtData.nll > y_puwg || nmtData.nur < y_puwg) { // Punkt poza granicą bieżącego kwadratu. Coord.convertPUWG1992ToWGS84(y_puwg, x_puwg, geoCoord); - x = Coord.convertLonTOGridX(geoCoord.lon); + x = Coord.convertLonToGridX(geoCoord.lon); y = Coord.convertLatToGridY(geoCoord.lat); final int x1 = x; final int y1 = y; - nmtData = nmtDataHashMap.computeIfAbsent(new Coord.Grid(x, y), k -> new NMTData(x1, y1, 0, 0)); + nmtData = nmtDataHashMap.computeIfAbsent(new Coord.Grid(x, y), k -> new NmtData(x1, y1, 0, 0)); if (nmtData.nur == 0) { // Kwadrat jeszcze nie był odczytany (czysty). // Współrzędne geo środka kwadratu. @@ -385,7 +392,7 @@ public class NMTDataReader { } } - private static void readXYZ(BufferedReader br, String firstLine, HashMap nmtDataHashMap) throws IOException { + private static void readXYZ(BufferedReader br, String firstLine, HashMap nmtDataHashMap) throws IOException { Coord.Puwg puwgCoord = new Coord.Puwg(); Coord.Geo geo = new Coord.Geo(); String line = firstLine; @@ -401,7 +408,7 @@ public class NMTDataReader { double h; int x; int y; - NMTData nmtData = new NMTData(-1, -1, 0, 0); + NmtData nmtData = new NmtData(-1, -1, 0, 0); int start; int end; int row = 0; @@ -434,11 +441,11 @@ public class NMTDataReader { if (nmtData.ell > x_puwg || nmtData.eur < x_puwg || nmtData.nll > y_puwg || nmtData.nur < y_puwg) { // Punkt poza granicą bieżącego kwadratu. Coord.convertPUWG1992ToWGS84(y_puwg, x_puwg, geo); - x = Coord.convertLonTOGridX(geo.lon); + x = Coord.convertLonToGridX(geo.lon); y = Coord.convertLatToGridY(geo.lat); final int x1 = x; final int y1 = y; - nmtData = nmtDataHashMap.computeIfAbsent(new Coord.Grid(x1, y1), k -> new NMTData(x1, y1, 0, 0)); + nmtData = nmtDataHashMap.computeIfAbsent(new Coord.Grid(x1, y1), k -> new NmtData(x1, y1, 0, 0)); if (nmtData.nur == 0) { // Kwadrat jeszcze nie był odczytany (czysty). // Współrzędne geo środka kwadratu. diff --git a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataProvider.java b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataProvider.java similarity index 96% rename from src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataProvider.java rename to src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataProvider.java index e8e233e..5fef33a 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/nmt/NMTDataProvider.java +++ b/src/main/java/pl/wat/ms4ds/terrain/nmt/NmtDataProvider.java @@ -18,8 +18,11 @@ import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -public class NMTDataProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(NMTDataProvider.class); +/** + * Klasa odpowiedzialna za pozyskanie danych wysokościowych (NMT) ze stron geoportal. + */ +public class NmtDataProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(NmtDataProvider.class); public static void main(String[] args) throws Exception { @@ -35,7 +38,7 @@ public class NMTDataProvider { // HashMap map = new HashMap<>(); // String fn0 = "D:/nmt/gugik_SkorowidzNMT20"; // for (int i = 18; i < 26; i++) { -// readFileLinksFromGUGiKxml(fn0 + i + ".gml", map); +// readFileLinksFromGugikXml(fn0 + i + ".gml", map); // } // saveLinks("D:/nmt/gugik_links.txt", map); @@ -161,7 +164,7 @@ public class NMTDataProvider { // Funkcja wykorzystana jednokrotnie w celu integracji w jednym pliku wszystkich linków // do zasobów rozproszonych w wielu plikach. - private static void readFileLinksFromGUGiKxml(String fn, HashMap map) throws Exception { + private static void readFileLinksFromGugikXml(String fn, HashMap map) throws Exception { XMLInputFactory factory = XMLInputFactory.newInstance(); FileInputStream is = new FileInputStream(fn); BufferedInputStream bis = new BufferedInputStream(is); diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordTest.java b/src/main/java/pl/wat/ms4ds/terrain/osm/CoordTest.java similarity index 98% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordTest.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/CoordTest.java index 0dbec75..2264ae9 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordTest.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/CoordTest.java @@ -1,12 +1,8 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.wat.ms4ds.terrain.Coord; -import pl.wat.ms4ds.terrain.MapConsts; - -import java.io.*; -import java.nio.ByteBuffer; public class CoordTest { static Logger logger = LoggerFactory.getLogger(CoordTest.class); diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordUtils.java b/src/main/java/pl/wat/ms4ds/terrain/osm/CoordUtils.java similarity index 91% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordUtils.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/CoordUtils.java index 1ac160b..c029b05 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/CoordUtils.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/CoordUtils.java @@ -1,10 +1,10 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import pl.wat.ms4ds.terrain.Coord; import pl.wat.ms4ds.terrain.Teren; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pl.wat.ms4ds.terrain.nmt.NMTData; +import pl.wat.ms4ds.terrain.nmt.NmtData; import java.io.BufferedReader; import java.io.FileReader; @@ -26,7 +26,7 @@ public class CoordUtils { public static void main(String[] args) throws Exception { - HashMap daneWysokHashMap = new HashMap(); + HashMap daneWysokHashMap = new HashMap(); if (args.length > 0) { dataDir = args[0]; } @@ -34,7 +34,7 @@ public class CoordUtils { String nmt_fn = args[i]; daneWysokHashMap.clear(); readData(nmt_fn, daneWysokHashMap); - for (NMTData daneWysok : daneWysokHashMap.values()) { + for (NmtData daneWysok : daneWysokHashMap.values()) { // Square kw = Teren.getKwadrat(daneWysok.idKw.x, daneWysok.idKw.y); // kw.setWysokoscSrednia((int) (daneWysok.suma / daneWysok.licz + 0.5)); } @@ -47,7 +47,7 @@ public class CoordUtils { } - private static void readData(String fileName, HashMap daneWysokHashMap) throws IOException { + private static void readData(String fileName, HashMap daneWysokHashMap) throws IOException { try { StringBuilder sb = new StringBuilder(100); sb.append(dataDir); @@ -90,9 +90,9 @@ public class CoordUtils { } Coord.convertPUWG1992ToWGS84(puwgCoord.northing, puwgCoord.easting, latLon); Coord.Grid idKw = new Coord.Grid(latLon.lon, latLon.lat); - NMTData daneWysok = daneWysokHashMap.get(idKw); + NmtData daneWysok = daneWysokHashMap.get(idKw); if (daneWysok == null) { - daneWysok = new NMTData(idKw.x, idKw.y, wysokosc, 1); + daneWysok = new NmtData(idKw.x, idKw.y, wysokosc, 1); daneWysokHashMap.put(idKw, daneWysok); } else { daneWysok.sum += wysokosc; diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EAreaFeature.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EAreaFeature.java similarity index 66% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EAreaFeature.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EAreaFeature.java index 26d0594..443c6d5 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EAreaFeature.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EAreaFeature.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EAreaFeature { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/ELinearFeature.java b/src/main/java/pl/wat/ms4ds/terrain/osm/ELinearFeature.java similarity index 62% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/ELinearFeature.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/ELinearFeature.java index 42cdaef..b1e613c 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/ELinearFeature.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/ELinearFeature.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum ELinearFeature { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMAmenity.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMAmenity.java similarity index 97% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMAmenity.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMAmenity.java index d827d96..2257126 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMAmenity.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMAmenity.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMAmenity { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBridge.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBridge.java similarity index 88% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBridge.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBridge.java index 0efd79f..cba8d62 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBridge.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBridge.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMBridge { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBuilding.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBuilding.java similarity index 94% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBuilding.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBuilding.java index b4ed424..479eb39 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMBuilding.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMBuilding.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMBuilding { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMHighway.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMHighway.java similarity index 97% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMHighway.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMHighway.java index 0134c02..f8c038b 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMHighway.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMHighway.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMHighway { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLandcover.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLandcover.java similarity index 90% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLandcover.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLandcover.java index 2a0aa9d..2dca705 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLandcover.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLandcover.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMLandcover { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLanduse.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLanduse.java similarity index 91% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLanduse.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLanduse.java index b71135f..da19808 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMLanduse.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMLanduse.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMLanduse { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMNatural.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMNatural.java similarity index 91% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMNatural.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMNatural.java index 1a8c495..e291c00 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMNatural.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMNatural.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMNatural { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWater.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWater.java similarity index 93% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWater.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWater.java index cd52718..68b504d 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWater.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWater.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMWater { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWaterway.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWaterway.java similarity index 93% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWaterway.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWaterway.java index bdc67c3..93411fe 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EOSMWaterway.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EOSMWaterway.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; public enum EOSMWaterway { diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EsriFileReader.java b/src/main/java/pl/wat/ms4ds/terrain/osm/EsriFileReader.java similarity index 94% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/EsriFileReader.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/EsriFileReader.java index aee89ac..7afe0bc 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/EsriFileReader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/EsriFileReader.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import org.nocrala.tools.gis.data.esri.shapefile.ShapeFileReader; import org.nocrala.tools.gis.data.esri.shapefile.ValidationPreferences; @@ -88,19 +88,19 @@ public class EsriFileReader { nodeId++; currNode.lon = points[j].getX() + 180; currNode.lat = points[j].getY() + 90; - if (currNode.lon >= MapConsts.LON_REF && currNode.lon <= MapConsts.LON_REF + MapConsts.DELTA_LON_REF - && currNode.lat >= MapConsts.LAT_REF && currNode.lat <= MapConsts.LAT_REF + MapConsts.DELTA_LAT_REF) { + if (currNode.lon >= MapConsts.REF_LON && currNode.lon <= MapConsts.REF_LON + MapConsts.DELTA_LON_REF + && currNode.lat >= MapConsts.REF_LAT && currNode.lat <= MapConsts.REF_LAT + MapConsts.DELTA_LAT_REF) { jest_wezel_z_obszaru = true; } - currNode.lon = Math.max(currNode.lon, MapConsts.LON_REF); - currNode.lon = Math.min(currNode.lon, MapConsts.LON_REF + MapConsts.DELTA_LON_REF); + currNode.lon = Math.max(currNode.lon, MapConsts.REF_LON); + currNode.lon = Math.min(currNode.lon, MapConsts.REF_LON + MapConsts.DELTA_LON_REF); currNode.lon -= 180; - currNode.lat = Math.max(currNode.lat, MapConsts.LAT_REF); - currNode.lat = Math.min(currNode.lat, MapConsts.LAT_REF + MapConsts.DELTA_LAT_REF); + currNode.lat = Math.max(currNode.lat, MapConsts.REF_LAT); + currNode.lat = Math.min(currNode.lat, MapConsts.REF_LAT + MapConsts.DELTA_LAT_REF); currNode.lat -= 90; - currNode.idX = Coord.zamienDlugoscGeoNaIdKwadratuX(currNode.lon); - currNode.idY = Coord.zamienSzerokoscGeoNaIdKwadratuY(currNode.lat); + currNode.idX = Coord.convertLonToGridX(currNode.lon); + currNode.idY = Coord.convertLatToGridY(currNode.lat); if (currNode.idX > 0 || currNode.idY > 0) { currWay.nodes.add(currNode); } diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/MapBounds.java b/src/main/java/pl/wat/ms4ds/terrain/osm/MapBounds.java similarity index 74% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/MapBounds.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/MapBounds.java index 540522e..031f735 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/MapBounds.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/MapBounds.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; /** * diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Node.java b/src/main/java/pl/wat/ms4ds/terrain/osm/Node.java similarity index 91% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/Node.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/Node.java index f59a5a5..442354d 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Node.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/Node.java @@ -1,8 +1,9 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import pl.wat.ms4ds.terrain.Square; import pl.wat.ms4ds.terrain.MapConsts; import pl.wat.ms4ds.terrain.Teren; +import pl.wat.ms4ds.terrain.TerrainType; /** * @@ -70,10 +71,7 @@ public class Node { public void writeAreaFeatureIntoSquare(EAreaFeature type) { if (buildingsCount >= BUILDINGS_COUNT) { Square kw = Teren.getSquare(idX, idY); - kw.stopienZabudowy = 1.0f; - } else if (buildingsCount > 0) { - Square kw = Teren.getSquare(idX, idY); - kw.stopienZabudowy = 0.5f; + kw.terrainType = TerrainType.BUILDINGS; } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/OpenStreetMapReader.java b/src/main/java/pl/wat/ms4ds/terrain/osm/OpenStreetMapReader.java similarity index 99% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/OpenStreetMapReader.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/OpenStreetMapReader.java index 487ed9c..3107d4c 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/OpenStreetMapReader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/OpenStreetMapReader.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import pl.wat.ms4ds.terrain.Coord; import pl.wat.ms4ds.terrain.Teren; @@ -238,8 +238,8 @@ public class OpenStreetMapReader { switch (reader.getLocalName()) { case "node": if (currNode != null) { - currNode.idX = Coord.zamienDlugoscGeoNaIdKwadratuX(currNode.lon); - currNode.idY = Coord.zamienSzerokoscGeoNaIdKwadratuY(currNode.lat); + currNode.idX = Coord.convertLonToGridX(currNode.lon); + currNode.idY = Coord.convertLatToGridY(currNode.lat); if ("zabudowa".equals(genGoal) && currNode.buildingsCount > 0) { buildingNodesMap.put(currNode.id, currNode); } diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Relation.java b/src/main/java/pl/wat/ms4ds/terrain/osm/Relation.java similarity index 99% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/Relation.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/Relation.java index 28e2a75..e3414c1 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Relation.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/Relation.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Way.java b/src/main/java/pl/wat/ms4ds/terrain/osm/Way.java similarity index 79% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/Way.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/Way.java index 7eb7f54..1b24968 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Way.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/Way.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import pl.wat.ms4ds.common.EGeoDirection; import pl.wat.ms4ds.terrain.*; @@ -148,47 +148,47 @@ public class Way { if (nodes.size() < 3) { return; } - boolean toDelete[] = new boolean[nodes.size()]; - Node node_i; - Node node_i_next; - Node node_i_prev; + boolean[] toDelete = new boolean[nodes.size()]; + Node curr_node; + Node next_node; + Node prev_node; boolean collinearFound = false; - int dlMax = nodes.size(); + int lenMax = nodes.size(); int iStart = 0; if (!isPolygon) { // łamana otwarta - dlMax = nodes.size() - 1; + lenMax = nodes.size() - 1; iStart = 1; } - for (int i = iStart; i < dlMax; i++) { - int i_plus_1 = (i + 1) % nodes.size(); - int i_minus_1 = (i + nodes.size() - 1) % nodes.size(); - node_i = nodes.get(i); - node_i_next = nodes.get(i_plus_1); - node_i_prev = nodes.get(i_minus_1); - if (GeomUtils.include(node_i_prev.idX, node_i_prev.idY, node_i_next.idX, node_i_next.idY, - node_i.idX, node_i.idY)) { + for (int curr = iStart; curr < lenMax; curr++) { + int next = (curr + 1) % nodes.size(); + int prev = (curr + nodes.size() - 1) % nodes.size(); + curr_node = nodes.get(curr); + next_node = nodes.get(next); + prev_node = nodes.get(prev); + if (GeomUtils.include(prev_node.idX, prev_node.idY, next_node.idX, next_node.idY, + curr_node.idX, curr_node.idY)) { // i-ty do usuniecia - toDelete[i] = true; + toDelete[curr] = true; collinearFound = true; - } else if (GeomUtils.include(node_i.idX, node_i.idY, node_i_next.idX, node_i_next.idY, - node_i_prev.idX, node_i_prev.idY)) { + } else if (GeomUtils.include(curr_node.idX, curr_node.idY, next_node.idX, next_node.idY, + prev_node.idX, prev_node.idY)) { // i-1-ty do usuniecia - toDelete[i_minus_1] = true; + toDelete[prev] = true; collinearFound = true; - } else if (GeomUtils.include(node_i_prev.idX, node_i_prev.idY, node_i.idX, node_i.idY, - node_i_next.idX, node_i_next.idY)) { + } else if (GeomUtils.include(prev_node.idX, prev_node.idY, curr_node.idX, curr_node.idY, + next_node.idX, next_node.idY)) { // i+1-ty do usuniecia - toDelete[i_plus_1] = true; + toDelete[next] = true; collinearFound = true; } } if (collinearFound) { ArrayList newList = new ArrayList(); for (int i = 0; i < nodes.size(); i++) { - node_i = nodes.get(i); + curr_node = nodes.get(i); if (!toDelete[i]) { - newList.add(node_i); + newList.add(curr_node); } } // logger.trace("Liczba oryg. wezlow= {}, liczba niewspolliniowych wezlow= {}, roznica= {}", nodes.size(), newList.size(), nodes.size() - newList.size()); @@ -203,20 +203,20 @@ public class Way { } boolean toDelete[] = new boolean[nodes.size()]; Node node_i; - Node node_j; - Node node_k; + Node node_ii; + Node node_iii; boolean bylSchodek = false; for (int i = 0; i < nodes.size() - 2; i++) { node_i = nodes.get(i); - node_j = nodes.get(i + 1); - node_k = nodes.get(i + 2); - int absX_i_j = Math.abs(node_j.idX - node_i.idX); - int absY_i_j = Math.abs(node_j.idY - node_i.idY); - int absX_j_k = Math.abs(node_k.idX - node_j.idX); - int absY_j_k = Math.abs(node_k.idY - node_j.idY); - if (absX_i_j + absY_i_j + absX_j_k + absY_j_k == 2) { + node_ii = nodes.get(i + 1); + node_iii = nodes.get(i + 2); + int absX_i_ii = Math.abs(node_ii.idX - node_i.idX); + int absY_i_ii = Math.abs(node_ii.idY - node_i.idY); + int absX_ii_iii = Math.abs(node_iii.idX - node_ii.idX); + int absY_ii_iii = Math.abs(node_iii.idY - node_ii.idY); + if (absX_i_ii + absY_i_ii + absX_ii_iii + absY_ii_iii == 2) { // wezly moga tworzyc schodek - if (absX_i_j + absX_j_k == 1) { + if (absX_i_ii + absX_ii_iii == 1) { // wezly tworza schodek, zatem srodkowy wezel schodka do usuniecia toDelete[i + 1] = true; bylSchodek = true; @@ -253,7 +253,7 @@ public class Way { Coord.Grid id0; Coord.Grid id1; EGeoDirection kier; - Coord.Grid[] kwadraty = GeomUtils.kwadratyLamanej2(punktyLamanej); + Coord.Grid[] kwadraty = GeomUtils.generateSquaresOfSegments(punktyLamanej); // float dlug = GeomUtils.dlugoscDrogiPoKwadratch(kwadraty); for (int i = 0; i < kwadraty.length - 1; i++) { try { @@ -264,16 +264,16 @@ public class Way { kier = GeomUtils.kierunekDlaSasiada(id0, id1); switch (type) { case ROAD: - kw0.jestDroga[kier.id] = true; - kw0.jestDroga[kier.oppositeDirect().id] = true; + kw0.roads[kier.id] = 2; + kw0.roads[kier.oppositeDirect().id] = 2; break; case WATER_WAY: - kw0.jestPrzeszkodaWodna[kier.id] = true; - kw0.jestPrzeszkodaWodna[kier.oppositeDirect().id] = true; + kw0.watercourses[kier.id] = 3; + kw0.watercourses[kier.oppositeDirect().id] = 3; break; case DITCH: - kw0.jestRow[kier.id] = true; - kw0.jestRow[kier.oppositeDirect().id] = true; + kw0.watercourses[kier.id] = 1; + kw0.watercourses[kier.oppositeDirect().id] = 1; break; default: } @@ -347,30 +347,27 @@ public class Way { maxX = Math.max(polygon[i].x, maxX); maxY = Math.max(polygon[i].y, maxY); } - Coord.Grid idTest = new Coord.Grid(); boolean inside; - for (int j = maxY; j >= minY; j--) { - for (int i = minX; i <= maxX; i++) { - idTest.x = i; - idTest.y = j; - Square kw = Teren.getSquare(idTest.x, idTest.y); + for (int y = maxY; y >= minY; y--) { + for (int x = minX; x <= maxX; x++) { + Square kw = Teren.getSquare(x, y); if (kw == Square.EMPTY) { continue; } - inside = GeomUtils.insidePolygon(polygon, idTest); + inside = GeomUtils.insidePolygon(polygon, x, y); if (inside) { switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } @@ -382,30 +379,27 @@ public class Way { public static void writeAreaFeatureIntoSquares(EAreaFeature type, boolean clearFeature, Coord.Grid[] polygon, int minX, int maxX, int minY, int maxY) { float val = (clearFeature) ? 0.0f : 1.0f; - Coord.Grid idTest = new Coord.Grid(); boolean inside; - for (int i = minX; i <= maxX; i++) { - for (int j = minY; j <= maxY; j++) { - idTest.x = i; - idTest.y = j; - Square kw = Teren.getSquare(idTest.x, idTest.y); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + Square kw = Teren.getSquare(x, y); if (kw == Square.EMPTY) { continue; } - inside = GeomUtils.insidePolygon(polygon, idTest); + inside = GeomUtils.insidePolygon(polygon, x, y); if (inside) { switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } @@ -424,38 +418,38 @@ public class Way { kw = Teren.getSquare(nodes.get(0).idX, nodes.get(0).idY); switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } return; } if (nodes.size() == 2) { - Coord.Grid[] kwadraty = GeomUtils.kwadratyOdcinka(nodes.get(0).idX, nodes.get(0).idY, + Coord.Grid[] kwadraty = GeomUtils.generateSquaresOfSegment(nodes.get(0).idX, nodes.get(0).idY, nodes.get(1).idX, nodes.get(1).idY); for (int i = 0; i < kwadraty.length; i++) { kw = Teren.getSquare(kwadraty[i].x, kwadraty[i].y); switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } @@ -479,38 +473,38 @@ public class Way { kw = Teren.getSquare(nodes.get(0).idX, nodes.get(0).idY); switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } return; } if (nodes.size() == 2) { - Coord.Grid[] kwadraty = GeomUtils.kwadratyOdcinka(nodes.get(0).idX, nodes.get(0).idY, + Coord.Grid[] kwadraty = GeomUtils.generateSquaresOfSegment(nodes.get(0).idX, nodes.get(0).idY, nodes.get(1).idX, nodes.get(1).idY); for (int i = 0; i < kwadraty.length; i++) { kw = Teren.getSquare(kwadraty[i].x, kwadraty[i].y); switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } @@ -543,37 +537,34 @@ public class Way { } return; } - Coord.Grid idTest = new Coord.Grid(); boolean nalezyDoWielokata; // int liczKw = 0; // int liczKwObszaru = 0; // for (int j = maxY; j >= minY; j--) { // for (int i = minX; i <= maxX; i++) { - for (int i = minX; i <= maxX; i++) { - for (int j = minY; j <= maxY; j++) { - idTest.x = i; - idTest.y = j; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { // char c = ' '; // liczKw++; - kw = Teren.getSquare(idTest.x, idTest.y); + kw = Teren.getSquare(x, y); if (kw == Square.EMPTY) { continue; } - nalezyDoWielokata = GeomUtils.insidePolygon(wielokat, idTest); + nalezyDoWielokata = GeomUtils.insidePolygon(wielokat, x, y); if (nalezyDoWielokata) { // c = 'O'; switch (type) { case FOREST: - kw.stopienZalesienia = val; + kw.terrainType = TerrainType.FOREST; break; case WATER: - kw.stopienZawodnienia = val; + kw.terrainType = TerrainType.WATER; break; case SWAMP: - kw.stopienZabagnienia = val; + kw.terrainType = TerrainType.SWAMP; break; case BUILDINGS: - kw.stopienZabudowy = val; + kw.terrainType = TerrainType.BUILDINGS; break; default: } diff --git a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Worker.java b/src/main/java/pl/wat/ms4ds/terrain/osm/Worker.java similarity index 66% rename from src/main/java/pl/wat/ms4ds/terrain/konwersja/Worker.java rename to src/main/java/pl/wat/ms4ds/terrain/osm/Worker.java index 9fb8fb3..5a7f68d 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/konwersja/Worker.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osm/Worker.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.konwersja; +package pl.wat.ms4ds.terrain.osm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,14 +40,10 @@ public class Worker extends Thread { @Override public void run() { try { - int ileKwTest = (maxX - minX) * (maxY - minY); - synchronized (OpenStreetMapReader.synch) { - logger.debug(Thread.currentThread().getName() + " >>> polygon.lent= " + polygon.length + ", ileKwTest= " + Integer.toString(ileKwTest)); - } + int ileKwTest = (maxX - minX + 1) * (maxY - minY + 1); + logger.debug("{} >>> polygon.lent= {}, ileKwTest= {}", Thread.currentThread().getName(), polygon.length, ileKwTest); Way.writeAreaFeatureIntoSquares(type, clearFeature, polygon, minX, maxX, minY, maxY); - synchronized (OpenStreetMapReader.synch) { - logger.debug(Thread.currentThread().getName() + " <<< polygon.lent= " + polygon.length + ", ileKwTest= " + Integer.toString(ileKwTest)); - } + logger.debug("{} <<< polygon.lent= {}, ileKwTest= {}", Thread.currentThread().getName(), polygon.length, ileKwTest); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfRecord.java b/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfRecord.java deleted file mode 100644 index 74a4f06..0000000 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfRecord.java +++ /dev/null @@ -1,66 +0,0 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; - -import java.io.BufferedInputStream; -import java.nio.charset.StandardCharsets; - -public class DbfRecord { - - /** - * OSM Id taken from the Id of this feature (node_id, way_id, or relation_id) in the - * OSM database. - * VARCHAR (10 Bytes) - */ - String osmId; - - /** - * Digit code (between 1000 and 9999) defining the feature class. The first one or - * two digits define the layer, the last two or three digits the class inside a layer. - * - */ - int code; - - /** - * Class name of this feature. This does not add any information that is not already - * in the “code” field but it is better readable. - */ - String fclass; - - /** - * Name of this feature, like a street or place name. If the name in OSM contains - * obviously wrong data such as “fixme" or “none”, it will be empty. - */ - String name; - - - public DbfRecord() throws Exception { - } - - public void read(BufferedInputStream bis, DbfHeader header) throws Exception { - byte[] data = new byte[header.recordSize]; - if (bis.read(data) != header.recordSize) { - throw new Exception("Invalid dbf file"); - } - String str = new String(data, StandardCharsets.UTF_8); - // Na pozycji 0 jest flag byte, dane startują od pozycji 1. - int from = 1; - int to = 1 + header.fields[0].size; - osmId = str.substring(from, to); - int endPos = osmId.indexOf(' '); - osmId = osmId.substring(0, endPos); - from = to; - to += header.fields[1].size; - String codeStr = str.substring(from, to); - code = Integer.parseInt(codeStr); - from = to; - to += header.fields[2].size; - fclass = str.substring(from, to); - endPos = fclass.indexOf(' '); - fclass = fclass.substring(0, endPos); - from = to; - to += header.fields[3].size; - name = str.substring(from, to); - endPos = name.indexOf(' '); - name = name.substring(0, endPos); - } - -} diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/Main.java b/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/Main.java deleted file mode 100644 index a9ffca6..0000000 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/Main.java +++ /dev/null @@ -1,23 +0,0 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; - - -public class Main { - - static void main(String[] args) { - try { - // GET DIRECTORY - String curDir = (args.length > 0) ? args[0] : ""; - String folder = "C:/Workspace/osm/dolnoslaskie-251217-free.shp/"; - - // LOAD SHAPE FILE (.shp, .shx, .dbf) - // gis_osm_buildings_a_free_1 - // gis_osm_water_a_free_1 - OsmShapeFileReader shapefile = new OsmShapeFileReader(curDir + folder, "gis_osm_buildings_a_free_1"); - ShpShape shape = shapefile.nextShape(); - System.out.println("Shape type = " + shape.getShapeType()); - } catch (Exception e) { - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfField.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfField.java similarity index 94% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfField.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfField.java index dc83e17..3b81af7 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfField.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfField.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; public class DbfField { diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfHeader.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfHeader.java similarity index 98% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfHeader.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfHeader.java index e0409e7..3891d20 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/DbfHeader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfHeader.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.io.BufferedInputStream; import java.io.UnsupportedEncodingException; diff --git a/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfRecord.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfRecord.java new file mode 100644 index 0000000..a330cac --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/DbfRecord.java @@ -0,0 +1,130 @@ +package pl.wat.ms4ds.terrain.osmshp; + +import java.io.BufferedInputStream; +import java.nio.charset.StandardCharsets; + +public class DbfRecord { + + /** + * OSM Id taken from the Id of this feature (node_id, way_id, or relation_id) in the + * OSM database. + * VARCHAR (10 Bytes) + */ + String osmId; + + /** + * Digit code (between 1000 and 9999) defining the feature class. The first one or + * two digits define the layer, the last two or three digits the class inside a layer. + */ + int code; + + /** + * Class name of this feature. This does not add any information that is not already + * in the “code” field but it is better readable. + */ + String fclass; + + /** + * Name of this feature, like a street or place name. + */ + String name; + + String roadRef; + boolean oneway; + int roadMaxSpeed; + int roadLayer; + boolean bridge; + boolean tunnel; + + int waterwayWidth; + String buildingType; + + + public DbfRecord() throws Exception { + } + + public void read(BufferedInputStream bis, DbfHeader header) throws Exception { + byte[] data = new byte[header.recordSize]; + if (bis.read(data) != header.recordSize) { + throw new Exception("Invalid dbf file"); + } + // Zamiana znaków spoza zakresu UTF-8 (ujemnych) na spację. + for (int i = 0; i < data.length; i++) { + if (data[i] < 0) { + data[i] = 32; + } + } + String str = new String(data, StandardCharsets.UTF_8); + // Na pozycji 0 jest tzw. flag byte, dane startują od pozycji 1. + int from = 1; + int to = from + header.fields[0].size; + // Pomijam czytanie osm id, gdyż nie jest wykorzystywane. +// osmId = str.substring(from, to); +// int endPos = osmId.indexOf(' '); +// osmId = osmId.substring(0, endPos); + from = to; + to += header.fields[1].size; + String codeStr = str.substring(from, to); + code = Integer.parseInt(codeStr); + from = to; + to += header.fields[2].size; + fclass = str.substring(from, to); + fclass = fclass.substring(0, fclass.indexOf(' ')); + from = to; + to += header.fields[3].size; + name = str.substring(from, to); + if (header.fields.length > 4) { + String s; + for (int i = 4; i < header.fields.length; i++) { + from = to; + to += header.fields[i].size; + s = str.substring(from, to); + if (header.fields[i].type == 'N') { + // Wartość numeryczna (int), zatem usuwam poprzedzające spacje + s = s.stripLeading(); + } + switch (header.fields[i].name) { + case "ref": + roadRef = s; + break; + case "oneway": + oneway = !s.equals("B"); + break; + case "maxspeed": + roadMaxSpeed = Integer.parseInt(s); + break; + case "layer": + roadLayer = Integer.parseInt(s); + break; + case "bridge": + bridge = s.equals("T"); + break; + case "tunnel": + tunnel = s.equals("T"); + break; + case "width": + waterwayWidth = Integer.parseInt(s); + break; + case "type": + buildingType = s; + break; + default: + } + } + } + + // Additional attributes for roads + //ref - VARCHAR(20) Reference number of this road ('A 5', 'L 605', ...) ref=* + //oneway VARCHAR(1) Is this a oneway road? “F” means that only driving + //in direction of the linestring is allowed. “T” means + //that only the opposite direction is allowed. “B” + //(default value) means that both directions are ok. + //oneway=* + //maxspeed SMALLINT Max allowed speed in km/h maxspeed=* + //layer SMALLINT Relative layering of roads (-5, ..., 0, ..., 5) layer=* + //bridge VARCHAR(1) Is this road on a bridge? (“T” = true, “F” = false) bridge=* + //tunnel VARCHAR(1) Is this road in a tunnel? (“T” = true, “F” = false) tunnel=* + + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/OsmShapeFileReader.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShapeFileReader.java similarity index 75% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/OsmShapeFileReader.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShapeFileReader.java index d0b64a3..b0b1087 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/OsmShapeFileReader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShapeFileReader.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.io.BufferedInputStream; import java.io.File; @@ -49,9 +49,10 @@ public class OsmShapeFileReader { // wetland 8221 /** + * Konstruktor obiektu readera danych OSM Shp. + * *
      * init the ShapeFile, and load the following files:
-     *   "path + filename.shx",
      *   "path + filename.dbf",
      *   "path + filename.shp"
      * 
@@ -63,16 +64,21 @@ public class OsmShapeFileReader { public OsmShapeFileReader(String path, String filename) throws Exception { File dir = new File(path); File shpFile = new File(dir, filename + ".shp"); - FileInputStream is = new FileInputStream(shpFile); - bisShp = new BufferedInputStream(is); + bisShp = new BufferedInputStream(new FileInputStream(shpFile)); shpHeader = new ShpHeader(bisShp); File dbfFile = new File(dir, filename + ".dbf"); bisDbf = new BufferedInputStream(new FileInputStream(dbfFile)); dbfHeader = new DbfHeader(bisDbf); } + /** + * Metoda odczytuje i zwraca obiekt reprezentujący określony kształt wraz z dodatkową informacją. + * + * @return + * @throws Exception + */ public ShpShape nextShape() throws Exception { - boolean hasNext = false; + boolean hasNext; try { hasNext = hasNextShape(); } catch (IOException e) { @@ -82,44 +88,33 @@ public class OsmShapeFileReader { return null; } ShpShape shape = null; - DbfRecord info = null; + DbfRecord info; switch (shpHeader.shapeType) { case Point, PointZ, PointM: shape = new ShpPoint(shpHeader.shapeType); - shape.read(bisShp); - info = new DbfRecord(); - info.read(bisDbf, dbfHeader); - shape.setInfo(info); - return shape; + break; case PolyLine, PolyLineZ, PolyLineM: shape = new ShpPolyLine(shpHeader.shapeType); - shape.read(bisShp); - info = new DbfRecord(); - info.read(bisDbf, dbfHeader); - shape.setInfo(info); - return shape; + break; case Polygon, PolygonZ, PolygonM: shape = new ShpPolygon(shpHeader.shapeType); - shape.read(bisShp); - info = new DbfRecord(); - info.read(bisDbf, dbfHeader); - shape.setInfo(info); - return shape; + break; case MultiPoint, MultiPointZ, MultiPointM: shape = new ShpMultiPoint(shpHeader.shapeType); - shape.read(bisShp); - info = new DbfRecord(); - info.read(bisDbf, dbfHeader); - shape.setInfo(info); - return shape; + break; default: return shape; } - + shape.read(bisShp); + info = new DbfRecord(); + info.read(bisDbf, dbfHeader); + shape.setInfo(info); + return shape; } public boolean hasNextShape() throws IOException { return bisShp.available() > 0; } + } \ No newline at end of file diff --git a/src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShpDataGenerator.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShpDataGenerator.java new file mode 100644 index 0000000..e201bc9 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/OsmShpDataGenerator.java @@ -0,0 +1,581 @@ +package pl.wat.ms4ds.terrain.osmshp; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pl.wat.ms4ds.common.EGeoDirection; +import pl.wat.ms4ds.terrain.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class OsmShpDataGenerator { + private static final Logger logger = LoggerFactory.getLogger(OsmShpDataGenerator.class); + + static void main(String[] args) { + try { + // GET DIRECTORY + // "C:/Workspace/osm/warminsko-mazurskie-251217-free.shp/" + // "dolnoslaskie" + // "kujawsko-pomorskie" + // "lodzkie" + // "lubelskie" + // "lubuskie" + // "malopolskie" + // "mazowieckie" + // "opolskie" + // "podkarpackie" + // "podlaskie" + // "pomorskie" + // "slaskie" + // "swietokrzyskie" + // "warminsko-mazurskie" + // "wielkopolskie" + // "zachodniopomorskie" +// String[] regions = new String[]{"dolnoslaskie", "kujawsko-pomorskie", "lodzkie", "lubelskie", +// "lubuskie", "malopolskie", "mazowieckie", "opolskie", +// "podkarpackie", "podlaskie", "pomorskie", "slaskie", +// "swietokrzyskie", "warminsko-mazurskie", "wielkopolskie", "zachodniopomorskie"}; + + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + String ext = "-251217-free.shp/"; + String inDir = "C:/Workspace/osm/"; + String outDir = "D:/work/kwadraty_nmt/temp/100m/"; + +// final String[] areaLayers = new String[]{"landuse_a", "buildings_a", "water_a", "pois_a", "pofw_a"}; +// final String[] pointLayers = new String[]{"pois", "pofw"}; +// final String[] linearLayers = new String[]{"waterways", "roads"}; + final String[] areaLayers = new String[]{"landuse_a", "buildings_a", "water_a"}; + final String[] pointLayers = new String[]{"pois", "pofw"}; + final String[] linearLayers = new String[]{"waterways", "roads"}; + double d = 3 * MapConsts.SS_DELTA_LON; + +// final int concurrency = 2; +//// String layerName = "gis_osm_" + areaLayers[0] + "_free_1"; +// for (int j = 0; j < 8; j++) { +// Future[] futures = new Future[concurrency]; +// for (int i = 0; i < concurrency; i++) { +// final int ii = concurrency * j + i; +// futures[i] = executor.submit(() -> { +// for (int k = 0; k < areaLayers.length; k++) { +// logger.info("Work started: region= {}, layer= {}", regions[ii], areaLayers[k]); +// String ln = "gis_osm_" + areaLayers[k] + "_free_1"; +// HashSet>[] data = generateDataFromAreaLayer(inDir + regions[ii] + ext, ln); +// writeAreaFeatures(data); +// int size = 0; +// for (HashSet> datum : data) { +// if (datum != null) { +// size += datum.size(); +// datum.clear(); +// } +// } +// logger.info("Work finished: region= {}, layer= {}, data.size= {}", regions[ii], areaLayers[k]); +// } +// for (int k = 0; k < pointLayers.length; k++) { +// logger.info("Work started: region= {}, layer= {}", regions[ii], pointLayers[k]); +// String ln = "gis_osm_" + pointLayers[k] + "_free_1"; +// HashSet data = generateDataFromPointLayer(inDir + regions[ii] + ext, ln); +// writeAreaFeatureIntoSquares(TerrainType.BUILDINGS, false, data); +// int size = data.size(); +// data.clear(); +// logger.info("Work finished: region= {}, layer= {}, data.size= {}", regions[ii], pointLayers[k], size); +// } +// for (int k = 0; k < linearLayers.length; k++) { +// logger.info("Work started: region= {}, layer= {}", regions[ii], linearLayers[k]); +// String ln = "gis_osm_" + linearLayers[k] + "_free_1"; +// HashSet>[] data = generateDataFromLinearLayer(inDir + regions[ii] + ext, ln); +// writeLinearFeatures(data, k); +// int size = 0; +// for (HashSet> datum : data) { +// if (datum != null) { +// size += datum.size(); +// datum.clear(); +// } +// } +// logger.info("Work finished: region= {}, layer= {}, data.size= {}", regions[ii], linearLayers[k], size); +// } +// }); +// } +// for (Future future : futures) { +// future.get(); +// } +// Teren.saveToFiles(outDir); +// } + +// String layerName = "gis_osm_" + areaLayers[0] + "_free_1"; + String[] regions = new String[]{"dolnoslaskie", "kujawsko-pomorskie", "lodzkie", "lubelskie", + "lubuskie", "malopolskie", "opolskie", "mazowieckie", "warminsko-mazurskie", + "podkarpackie", "podlaskie", "pomorskie", "slaskie", + "swietokrzyskie", "wielkopolskie", "zachodniopomorskie"}; +// String[] regions = new String[]{"mazowieckie", "warminsko-mazurskie"}; + for (int i = 0; i < regions.length; i++) { + for (int k = 0; k < areaLayers.length; k++) { + logger.info("Work started: region= {}, layer= {}", regions[i], areaLayers[k]); + String ln = "gis_osm_" + areaLayers[k] + "_free_1"; + HashSet>[] data = generateDataFromAreaLayer(inDir + regions[i] + ext, ln); + writeAreaFeatures(data); + for (HashSet> datum : data) { + if (datum != null) { + datum.clear(); + } + } + logger.info("Work finished: region= {}, layer= {}", regions[i], areaLayers[k]); + } + for (int k = 0; k < pointLayers.length; k++) { + logger.info("Work started: region= {}, layer= {}", regions[i], pointLayers[k]); + String ln = "gis_osm_" + pointLayers[k] + "_free_1"; + HashSet data = generateDataFromPointLayer(inDir + regions[i] + ext, ln); + writeAreaFeatureIntoSquares(TerrainType.BUILDINGS, false, data); + data.clear(); + logger.info("Work finished: region= {}, layer= {}", regions[i], pointLayers[k]); + } + for (int k = 0; k < linearLayers.length; k++) { + logger.info("Work started: region= {}, layer= {}", regions[i], linearLayers[k]); + String ln = "gis_osm_" + linearLayers[k] + "_free_1"; + HashSet>[] data = generateDataFromLinearLayer(inDir + regions[i] + ext, ln); + writeLinearFeatures(data, k); + for (HashSet> datum : data) { + if (datum != null) { + datum.clear(); + } + } + logger.info("Work finished: region= {}, layer= {}", regions[i], linearLayers[k]); + } + Teren.saveToFiles(outDir); + } + +// HashSet>[] data = generateDataFromAreaLayer(inDir + regions[0] + ext, "gis_osm_pois_a_free_1"); +// writeAreaFeatures(data); +// String ln = "gis_osm_" + pointLayers[1] + "_free_1"; +// HashSet data2 = generateDataFromPointLayer(inDir + regions[0] + ext, ln); +// writeAreaFeatureIntoSquares(TerrainType.BUILDINGS, false, data2); + +// writeLinearFeatures(data, 1); +// Teren.saveToFiles(outDir); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Funkcja zapisuje dane dotyczace określonych charakterystyk terenowych + * (odpowiadające indeksom głównej tablicy) do kwadratów. + *

+ * Każda cecha przetwarzana jest osobno (indeks tablicy). W ramach cechy przetwarzane są obszary. + * Każdy obszar opisany jest listą wielokątów określających zewnętrzne granice oraz opcjonalnie dziury. + * + * @param data + */ + public static void writeAreaFeatures(HashSet>[] data) { + HashSet squareSet = new HashSet<>(); + // Indeksy odpowiadają numeracji (id) wartości w TerrainType. + for (int typeId = 1; typeId < data.length; typeId++) { + HashSet> set = data[typeId]; + if (set == null || set.isEmpty()) { + continue; + } + TerrainType terrainType = TerrainType.valueFromId(typeId); + for (ArrayList area : set) { + collectSquaresOfArea(area, squareSet, true); + writeAreaFeatureIntoSquares(terrainType, false, squareSet); + squareSet.clear(); + } + } + } + + /** + * Funkcja gromadzi współrzędne (gridowe) kwadratów należących do obszaru opisanego wielokątami. + * Pierwszy wielokąt opisuje zewnętrzne granice obszaru. + * Kolejne wielokąty opisują granice obszarów wewnętrznych, będących dziurami. + * + * @param area + * @param squareSet + */ + public static void collectSquaresOfArea(ArrayList area, HashSet squareSet, boolean onlyOuter) { + Coord.Grid[] outerPolygon = area.getFirst(); + if (outerPolygon == null) { + return; + } + int[] bounds = GeomUtils.getBounds(outerPolygon); + int minX = bounds[0]; + int minY = bounds[1]; + int maxX = bounds[2]; + int maxY = bounds[3]; + boolean inside; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + inside = GeomUtils.insidePolygon(outerPolygon, x, y); + if (inside) { + squareSet.add(new Coord.Grid(x, y)); + } + } + } + if (onlyOuter) { + return; + } + int count = squareSet.size(); + // Pozostałe wielokąty opisują obszary wewnętrzne będące tzw. dziurami. + boolean removed; + Coord.Grid[] innerPolygon; + Coord.Grid test; + for (int i = 1; i < area.size(); i++) { + innerPolygon = area.get(i); + bounds = GeomUtils.getBounds(innerPolygon); + minX = bounds[0]; + minY = bounds[1]; + maxX = bounds[2]; + maxY = bounds[3]; + test = new Coord.Grid(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + inside = GeomUtils.insidePolygon(outerPolygon, x, y); + if (inside) { + test.x = x; + test.y = y; + removed = squareSet.remove(test); + } + } + } + } + int countOfRemoved = count - squareSet.size(); + logger.trace("Num of outer= {}, num of inner= {}", count, countOfRemoved); + + } + + public static void writeAreaFeatureIntoSquares(TerrainType type, boolean clearFeature, HashSet squareSet) { + TerrainType val = (clearFeature) ? TerrainType.NONE : type; + for (Coord.Grid coord : squareSet) { + Square square = Teren.getSquare(coord.x, coord.y); + if (square == Square.EMPTY) { + continue; + } + synchronized (square) { + square.terrainType = val; + } + } + } + + /** + * Funkcja zwraca id wartości typu wyliczeniowego {@link TerrainType} dla obiektów obszarowych. + *

Wartość 0 - NONE, 1 - GRASS, 2 - SWAMP, 3 - WATER, 4 - SCRUB_BUSHES, 5 - BUILDINGS, 6 - FOREST. + * + * @param shpShape + * @return + */ + public static int mapFclassToTerrainType(ShpShape shpShape) { + if (shpShape.info.code == 1500) { + return TerrainType.BUILDINGS.id; + } + return switch (shpShape.info.fclass) { +// case "residential", "industrial", "commercial", "retail", "farmyard" -> TerrainType.BUILDINGS.id; + case "police", "fire_station", "post_office", "library", "town_hall", "courthouse", "prison", "embassy", + "theatre", "nightclub", "cinema", "nursing_home", "market_place", "university", "school", + "kindergarten", "college", "public_building", "pharmacy", "hospital", "clinic", "doctors", "dentist", + "veterinary", "restaurant", "fast_food", "cafe", "pub", "hotel", "motel", "bed_and_breakfast", + "guesthouse", "hostel", "supermarket", "bakery", "mall", "department_store", "bank", "museum", + "castle", "bookshop", "clothes", "general", "hairdresser", + "jeweller", "optician" -> TerrainType.BUILDINGS.id; + case "christian", "christian_anglican", "christian_catholic", "christian_evangelical", + "christian_lutheran", "christian_methodist", "christian_orthodox", "christian_protestant", + "christian_baptist", "christian_mormon" -> TerrainType.BUILDINGS.id; + case "forest", "woodland", "park", "orchard" -> TerrainType.FOREST.id; + case "grass", "meadow", "farmland", "recreation_ground", "heath" -> TerrainType.GRASS.id; + case "scrub", "vineyard", "allotments" -> TerrainType.SCRUB_BUSHES.id; + case "water", "reservoir", "river", "dock" -> TerrainType.WATER.id; + case "wetland" -> TerrainType.SWAMP.id; + default -> TerrainType.NONE.id; + }; + } + + /** + * Generuje dane obszarowe w zakresie typu terenu {@link TerrainType}. + *

+ * "gis_osm_landuse_a_free_1", "gis_osm_water_a_free_1", "gis_osm_buildings_a_free_1", "gis_osm_pois_a_free_1", "gis_osm_pofw_a_free_1" + * + * @param path + */ + static HashSet>[] generateDataFromAreaLayer(String path, String filename) { + HashSet>[] result = new HashSet[TerrainType.values().length]; + result[1] = new HashSet<>(); + result[2] = new HashSet<>(); + result[3] = new HashSet<>(); + result[4] = new HashSet<>(); + result[5] = new HashSet<>(); + result[6] = new HashSet<>(); + try { + OsmShapeFileReader osmShapeFileReader = new OsmShapeFileReader(path, filename); + int pos = 0; + int typeId; + while (osmShapeFileReader.hasNextShape()) { + ShpShape shape = osmShapeFileReader.nextShape(); + if (shape instanceof ShpPolygon shpPolygon) { + typeId = mapFclassToTerrainType(shpPolygon); + String fclass = shpPolygon.info.fclass; + if (shpPolygon.info.code == 1500) { + // Warstwa obszarowa dot. budynków nie ma zdefiniowanej fclass, zatem sprawdzam code + // i ewentualnie zmieniam typeId. + typeId = TerrainType.BUILDINGS.id; + fclass = "building1500"; + } + if (typeId == 0) { + continue; + } + logger.trace("Shape pos = {}, shape type = {}, fclass= {}, numParts= {}", pos, shape.getShapeType(), fclass, shpPolygon.numberOfParts); + ArrayList polygons = generatePolylines(shpPolygon); + result[typeId].add(polygons); + pos++; + } + } + logger.info("End, num={}", pos); + } catch (Exception e) { + } + return result; + } + + enum WatercourseType { + NONE(0), DITCH_DRAIN(1), STREAM(2), RIVER(3); + + public final int id; + + WatercourseType(int id) { + this.id = id; + } + + public static WatercourseType valueFromId(int id) { + return values[id]; + } + + static final WatercourseType[] values = WatercourseType.values(); + + } + + /** + * Funkcja zwraca id wartości typu wyliczeniowego dla obiektów liniowych typu cieki wodne oraz drogi. + *

Wartość 0 - brak, 1 - DITCH_DRAIN/SMALL, 2 - STREAM/MINOR, 3 - RIVER/MAJOR + * + * @param shpShape + * @return typeId of linear feature: 0 - NONE, 1 - DITCH_DRAIN/SMALL, 2 - STREAM/MINOR, 3 - RIVER/MAJOR + */ + public static int mapFclassToLineTypeId(ShpShape shpShape) { + return switch (shpShape.info.fclass) { + case "river" -> WatercourseType.RIVER.id; + case "stream", "canal" -> WatercourseType.STREAM.id; + case "drain" -> WatercourseType.DITCH_DRAIN.id; + case "motorway", "trunk", "primary", "secondary", "tertiary", "motorway_link", "trunk_link", "primary_link", + "secondary_link", "tertiary_link" -> RoadType.MAJOR.id; + case "unclassified", "residential", "living_street" -> RoadType.MINOR.id; + case "service", "track", "track_grade1", "track_grade2", "track_grade3", "track_grade4", "track_grade5" -> + RoadType.SMALL.id; + default -> 0; + }; + } + + /** + * Generuje dane liniowe w zakresie cieków/przeszkód wodnych. + * (RIVER, STREAM, DITCH_DRAIN). + * (MAJOR_ROADS, MINOR_ROADS, SMALL_ROADS) + *

+ * "gis_osm_waterways_free_1", "gis_osm_roads_free_1" + * + * @param path + */ + static HashSet>[] generateDataFromLinearLayer(String path, String filename) { + HashSet>[] result = new HashSet[]{new HashSet<>(), new HashSet<>(), new HashSet<>(), new HashSet<>()}; + int width; + try { + OsmShapeFileReader osmShapeFileReader = new OsmShapeFileReader(path, filename); + int pos = 0; + int typeId; + while (osmShapeFileReader.hasNextShape()) { + ShpShape shape = osmShapeFileReader.nextShape(); + typeId = mapFclassToLineTypeId(shape); + if (typeId == 0) { + continue; + } + if (shape instanceof ShpPolyLine shpPolyLine) { + width = shpPolyLine.info.waterwayWidth; + logger.trace("Shape pos = {}, shape type = {}, fclass= {}, numParts= {}, width= {}", pos, shape.getShapeType(), shape.info.fclass, shpPolyLine.numberOfParts, width); + ArrayList polylines = generatePolylines(shpPolyLine); + result[typeId].add(polylines); + pos++; + } + } + logger.info("End, num={}", pos); + } catch (Exception e) { + } + return result; + } + + enum RoadType { + NONE(0), SMALL(1), MINOR(2), MAJOR(3); + + public final int id; + + RoadType(int id) { + this.id = id; + } + + public static RoadType valueFromId(int id) { + return values[id]; + } + + static final RoadType[] values = RoadType.values(); + + } + + /** + * Funkcja generalna odpowiedzialna za przekształcenie danych liniowych i zapis do kwadratów. + * + * @param data + * @param featureCategory kategoria danych liniowych (0 - cieki wodne, 1 - drogi) + */ + public static void writeLinearFeatures(HashSet>[] data, int featureCategory) { + ArrayList polylineList = new ArrayList<>(); + // Indeksy odpowiadają numeracji (id) wartości w typach wyliczeniowych danej charakterystyki. + for (int typeId = 0; typeId < data.length; typeId++) { + HashSet> set = data[typeId]; + if (set == null || set.isEmpty()) { + continue; + } + for (ArrayList polylines : set) { + collectSquaresOfPolylines(polylines, polylineList); + writeLinearFeatureIntoSquares((byte) typeId, false, polylineList, featureCategory); + polylineList.clear(); + } + } + } + + /** + * Funkcja gromadzi sekwencje współrzędnych kwadratów tworzących łamane. + * + * @param polylines list of polylines defined by vertices + * @param polylineList set of detailed polylines defined by squares sequances + */ + public static void collectSquaresOfPolylines(ArrayList polylines, ArrayList polylineList) { + Coord.Grid[] squares; + for (Coord.Grid[] polyline : polylines) { + squares = GeomUtils.generateSquaresOfSegments(polyline); + squares = GeomUtils.removeSteps(squares); + polylineList.add(squares); + } + logger.trace("Num of polylines= {}", polylines.size()); + } + + /** + * + * @param type type of feature (depends on the feature category) + * @param clearFeature + * @param polylineList polylines defined by squares sequences + * @param featureCategory feature category to write (0 - watercourses, 1 - roads) + */ + public static void writeLinearFeatureIntoSquares(byte type, boolean clearFeature, ArrayList polylineList, int featureCategory) { + byte val = (clearFeature) ? 0 : type; + for (Coord.Grid[] grids : polylineList) { + for (int i = 0; i < grids.length - 1; i++) { + int x0 = grids[i].x; + int y0 = grids[i].y; + int x1 = grids[i + 1].x; + int y1 = grids[i + 1].y; + Square curr_square = Teren.getSquare(x0, y0); + EGeoDirection outDirection = GeomUtils.neighborDirection(x0, y0, x1, y1); + if (curr_square != Square.EMPTY) { + synchronized (curr_square) { + if (featureCategory == 0) { + curr_square.watercourses[outDirection.id] = val; + } else { + curr_square.roads[outDirection.id] = val; + } + } + } + EGeoDirection inDirection = outDirection.oppositeDirect(); + Square next_square = Teren.getSquare(x1, y1); + if (next_square != Square.EMPTY) { + synchronized (next_square) { + if (featureCategory == 0) { + next_square.watercourses[inDirection.id] = val; + } else { + next_square.roads[inDirection.id] = val; + } + } + } + } + } + } + + /** + * Generuje dane obszarowe w zakresie zabudowy {@link TerrainType}. + *

+ * "gis_osm_pois_free_1", "gis_osm_pofw_free_1" + * + * @param path + */ + static HashSet generateDataFromPointLayer(String path, String filename) { + HashSet result = new HashSet<>(); + try { + OsmShapeFileReader osmShapeFileReader = new OsmShapeFileReader(path, filename); + int pos = 0; + int typeId; + int x; + int y; + while (osmShapeFileReader.hasNextShape()) { + ShpShape shape = osmShapeFileReader.nextShape(); + if (shape instanceof ShpPoint shpPoint) { + typeId = mapFclassToTerrainType(shpPoint); + if (typeId != TerrainType.BUILDINGS.id) { + continue; + } + logger.trace("Shape pos = {}, shape type = {}, fclass= {}", pos, shape.getShapeType(), shape.info.fclass); + x = Coord.convertLonToGridX(shpPoint.x); + y = Coord.convertLatToGridY(shpPoint.y); + Coord.Grid coord = new Coord.Grid(x, y); + result.add(coord); + pos++; + } + } + logger.info("End, num={}", pos); + } catch (Exception e) { + } + return result; + } + + /** + * Funkcja generuje dane geometryczne na siatce kwadratów. + *

Lista łamanych (otwartych lub zamkniętych) opisujących kształt obiektu klasy {@link ShpPolyShape}. + *

W przypadku obiektów obszarowych pierwszy element opisuje zewnętrzną powłokę, kolejne tzw. dziury.

+ *

W przypadku obiektów liniowych zawiera jedną łamaną.

+ * + * @param shpPolyShape + * @return + */ + static ArrayList generatePolylines(ShpPolyShape shpPolyShape) { + ArrayList polygons = new ArrayList<>(); + int start; + int stop; + Coord.Grid[] polygon; + for (int i = 0; i < shpPolyShape.numberOfParts; i++) { + start = shpPolyShape.partsPos[i]; + int next = i + 1; + if (next < shpPolyShape.numberOfParts) { + stop = shpPolyShape.partsPos[next]; + } else { + stop = shpPolyShape.numberOfPoints; + } + polygon = new Coord.Grid[stop - start]; + for (int j = 0; j < polygon.length; j++) { + int x = Coord.convertLonToGridX(shpPolyShape.pointsX[start + j]); + int y = Coord.convertLatToGridY(shpPolyShape.pointsY[start + j]); + polygon[j] = new Coord.Grid(x, y); + } +// boolean b1 = GeomUtils.isClockwise(polygon); + polygon = GeomUtils.removeAdjacentDuplicates(polygon); + polygon = GeomUtils.removeCollinearity(polygon, true); +// boolean b2 = GeomUtils.isClockwise(polygon); + polygons.add(polygon); + logger.trace("Part id = {}, numOfPoints= {}", i, polygon.length); + } + return polygons; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpHeader.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpHeader.java similarity index 80% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpHeader.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpHeader.java index 0d72b8e..9a9500c 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpHeader.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpHeader.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.io.*; import java.nio.ByteBuffer; @@ -67,17 +67,17 @@ public class ShpHeader { } /** - * get the type ShapeType the shapeFile contains.
- * a shapeFile contains only one type of shape.
+ * Get the ShapeType the shapeFile contains.
+ * A shapeFile contains only one type of shape.
* - * @return ShpShape.Type + * @return the type of shape */ public ShpShape.Type getShapeType() { return shapeType; } /** - * data storage: [3][2] --> [x,y,z][min, max]. + * Data storage: [3][2] --> [x,y,z][min, max]. * * @return boundingbox as double[][] */ @@ -86,43 +86,41 @@ public class ShpHeader { } /** - * get measure range.
- * data storage: [2] --> [min, max] + * Get measure range.
+ * [min, max] * - * @return double[] + * @return measure range as double[] */ public double[] getMeasureRange() { return rangeM; } /** - * get length in bytes of the shapeFile. + * Get length in bytes of the shapeFile. * - * @return length in bytes. + * @return length in bytes */ - public int getFileLengthBytes() { + public int getFileLength() { return fileLength; } /** - * get Verions on the shapeFile. + * Get Verions on the shapeFile. * - * @return should return 1000. + * @return should return 1000 */ public int getVersion() { return SHAPE_FILE_VERSION; } /** - * get MAGIC NUMBER of shapeFile. + * Get MAGIC NUMBER of shapeFile. * - * @return should return 9994. + * @return should return 9994 */ public int getMagicNumber() { return SHAPE_FILE_CODE; } - public void print() { - } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpMultiPoint.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpMultiPoint.java similarity index 91% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpMultiPoint.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpMultiPoint.java index 6413ff6..299100d 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpMultiPoint.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpMultiPoint.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.nio.ByteBuffer; @@ -35,7 +35,7 @@ public class ShpMultiPoint extends ShpShape { } @Override - protected void readRecordContent(ByteBuffer bb) { + protected void readContent(ByteBuffer bb) { xMin = bb.getDouble(); // x-min yMin = bb.getDouble(); // y-min xMax = bb.getDouble(); // x-max @@ -49,7 +49,7 @@ public class ShpMultiPoint extends ShpShape { pointsY[i] = bb.getDouble(); // y - coordinate } // if SHAPE-TYPE: 18 - if (type.hasZvalues()) { + if (type.hasZ()) { zMin = bb.getDouble(); // z-min zMax = bb.getDouble(); // z-max for (int i = 0; i < numberOfPoints; i++) { @@ -57,7 +57,7 @@ public class ShpMultiPoint extends ShpShape { } } // if SHAPE-TYPE: 18 | 28 - if (type.hasMvalues()) { + if (type.hasM()) { mMin = bb.getDouble(); // m-min mMax = bb.getDouble(); // m-max valuesM = new double[numberOfPoints]; diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPoint.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPoint.java similarity index 81% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPoint.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPoint.java index 795da73..937093e 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPoint.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPoint.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.nio.ByteBuffer; @@ -28,15 +28,15 @@ public class ShpPoint extends ShpShape { } @Override - protected void readRecordContent(ByteBuffer bb) { + protected void readContent(ByteBuffer bb) { x = bb.getDouble(); // x - coordinate y = bb.getDouble(); // y - coordinate // if SHAPE-TYPE: 11 - if (type.hasZvalues()) { + if (type.hasZ()) { z = bb.getDouble(); // z - coordinate } // if SHAPE-TYPE: 11 | 21 - if (type.hasMvalues()) { + if (type.hasM()) { m = bb.getDouble(); // m - value } } diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolyLine.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyLine.java similarity index 83% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolyLine.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyLine.java index 8fb13b5..0a40d6b 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolyLine.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyLine.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; /** * Shape: PolyLine.
@@ -17,7 +17,7 @@ package pl.wat.ms4ds.terrain.osm.shapefile; * @author jrulka * */ -public class ShpPolyLine extends ShpPollyShape { +public class ShpPolyLine extends ShpPolyShape { public ShpPolyLine(Type shape_type) { super(shape_type); diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPollyShape.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyShape.java similarity index 80% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPollyShape.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyShape.java index f7345aa..f9b2763 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPollyShape.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolyShape.java @@ -1,11 +1,12 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.nio.ByteBuffer; /** - * Base class for PollyLine, Pollygon + * Base class for {@link ShpPolyLine}, {@link ShpPolygon}. + *

Contains common attributes and methods. */ -public abstract class ShpPollyShape extends ShpShape { +public abstract class ShpPolyShape extends ShpShape { // SHAPE RECORD CONTENT double xMin; double yMin; @@ -15,20 +16,20 @@ public abstract class ShpPollyShape extends ShpShape { double yMax; double zMax; double mMax; - private int numberOfParts; - private int numberOfPoints; - private int[] partsPos; + int numberOfParts; + int numberOfPoints; + int[] partsPos; double[] pointsX; // [number of points][x,y,z] double[] pointsY; double[] pointsZ; double[] valuesM; // [number of points][m-value] - public ShpPollyShape(ShpShape.Type shape_type) { + public ShpPolyShape(ShpShape.Type shape_type) { super(shape_type); } @Override - protected void readRecordContent(ByteBuffer bb) { + protected void readContent(ByteBuffer bb) { xMin = bb.getDouble(); // x-min yMin = bb.getDouble(); // y-min xMax = bb.getDouble(); // x-max @@ -46,7 +47,7 @@ public abstract class ShpPollyShape extends ShpShape { pointsY[i] = bb.getDouble(); // y - coordinate } // if SHAPE-TYPE: 13 - if (type.hasZvalues()) { + if (type.hasZ()) { zMin = bb.getDouble(); // z-min zMax = bb.getDouble(); // z-max for (int i = 0; i < numberOfPoints; i++) { @@ -54,7 +55,7 @@ public abstract class ShpPollyShape extends ShpShape { } } // if SHAPE-TYPE: 13 | 23 - if (type.hasMvalues()) { + if (type.hasM()) { mMin = bb.getDouble(); // m-min mMax = bb.getDouble(); // m-max valuesM = new double[numberOfPoints]; diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolygon.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolygon.java similarity index 83% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolygon.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolygon.java index 0bf7624..07576fd 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpPolygon.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpPolygon.java @@ -1,4 +1,4 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; /** @@ -17,7 +17,7 @@ package pl.wat.ms4ds.terrain.osm.shapefile; * @author jrulka * */ -public class ShpPolygon extends ShpPollyShape { +public class ShpPolygon extends ShpPolyShape { public ShpPolygon(Type shape_type) { super(shape_type); diff --git a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpShape.java b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpShape.java similarity index 77% rename from src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpShape.java rename to src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpShape.java index d82f304..e54ae85 100644 --- a/src/main/java/pl/wat/ms4ds/terrain/osm/shapefile/ShpShape.java +++ b/src/main/java/pl/wat/ms4ds/terrain/osmshp/ShpShape.java @@ -1,11 +1,12 @@ -package pl.wat.ms4ds.terrain.osm.shapefile; +package pl.wat.ms4ds.terrain.osmshp; import java.io.BufferedInputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** - * Base class for Shapes. + * Klasa bazowa reprezentująca kształt w formacie SHP wraz z dodatkową informacją opisową. + * Mozliwe typy kształtów zawiera klasa enumeratora {@link Type} * * @author jrulka * @@ -35,7 +36,7 @@ public abstract class ShpShape { } ByteBuffer bb = ByteBuffer.wrap(data); // 1) READ RECORD HEADER - readRecordHeader(bb); + readHeader(bb); // Content length in words (16 bits) byte[] dataContent = new byte[contentLength * 2]; if (bis.read(dataContent) != contentLength * 2) { @@ -49,7 +50,7 @@ public abstract class ShpShape { try { Type shape_type = Type.getByCode(shapeTypeCode); if (shape_type == type) { - readRecordContent(bb); + readContent(bb); } else if (shape_type != type) { throw new Exception("(Shape) shape_type = " + shape_type + ", but expected " + type); } @@ -58,20 +59,24 @@ public abstract class ShpShape { } } - protected void readRecordHeader(ByteBuffer bb) { + protected void readHeader(ByteBuffer bb) { bb.order(ByteOrder.BIG_ENDIAN); recordNumber = bb.getInt(); contentLength = bb.getInt(); } - protected abstract void readRecordContent(ByteBuffer bb); + /** + * Reads geometric data from buffer. + * @param bb + */ + protected abstract void readContent(ByteBuffer bb); void setInfo(DbfRecord info) { this.info = info; } /** - * get the record number of the shape. + * Gets the record number of the shape. * * @return record number */ @@ -80,17 +85,17 @@ public abstract class ShpShape { } /** - * get the Type of the Shape. + * Gets the type of the shape. * - * @return ShpShape.Type + * @return {@link Type} */ public Type getShapeType() { return type; } - //---------------------------------------------------------------------------- - // Shape Types - //---------------------------------------------------------------------------- + /** + * Shape types. + */ public enum Type { /** * ID= 0 @@ -151,28 +156,28 @@ public abstract class ShpShape { public final int code; - boolean has_z_values; - boolean has_m_values; + boolean hasZ; + boolean hasM; - Type(int code, boolean has_z_values, boolean has_m_values) { - this.has_z_values = has_z_values; - this.has_m_values = has_m_values; + Type(int code, boolean hasZ, boolean hasM) { + this.hasZ = hasZ; + this.hasM = hasM; this.code = code; } - public static Type getByCode(int ID) throws Exception { + public static Type getByCode(int code) throws Exception { for (Type st : Type.values()) - if (st.code == ID) + if (st.code == code) return st; - throw new Exception("ShapeType: " + ID + " does not exist"); + throw new Exception("ShapeType: " + code + " does not exist"); } - public boolean hasZvalues() { - return has_z_values; + public boolean hasZ() { + return hasZ; } - public boolean hasMvalues() { - return has_m_values; + public boolean hasM() { + return hasM; } public boolean isTypeOfPolygon() { diff --git a/teren.properties b/teren.properties index e164245..b120a41 100644 --- a/teren.properties +++ b/teren.properties @@ -5,12 +5,12 @@ y_ref=49 dx_ref=11 dy_ref=7 #kwadraty_dir=D:/work/terrain/ -kwadraty_dir=D:/work/kwadraty_nmt/withElevation/ +kwadraty_dir=D:/work/kwadraty_nmt/temp/ #kwadraty_dir=C:/Workspace/_data/swdt/ms4ds/teren/kwadraty/ drogi_dir=au2data/new_teren/Polska/drogi/ # #Rozdzielczosc terenu dl_mk=200 | 100 | 50 | 25 | 20 -dl_mk=50 +dl_mk=100 # #W celu wymuszenia (mimo jej braku) przejezdności terenu nalezy ustawić na: on przejezdnosc_zawsze=off