diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..eadf803 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..fade66b --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..2f41a33 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0fb7218 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..aa10024 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/terrain-utilities.iml b/.idea/terrain-utilities.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/terrain-utilities.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/log4j.properties b/log4j.properties new file mode 100644 index 0000000..07f0b8e --- /dev/null +++ b/log4j.properties @@ -0,0 +1,55 @@ +#Glowny logger, pierwsza wartosc to poziom logowania +#Logowane sa zdarzenia o poziomie wiekszym lub rownym danemu +#Hierarchia poziomow to: DEBUG + + + + %-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n + + + + terrain.log + true + + + %-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d9b2e49 --- /dev/null +++ b/pom.xml @@ -0,0 +1,183 @@ + + + 4.0.0 + + ms4ds + teren-funkcje + 1.0-SNAPSHOT + + + + nexus-homelab + nexus-homelab + https://nexus.rulka.pl/repository/maven-public/ + + + + + UTF-8 + + + + + + ${basedir} + + *.properties + logback.xml + + + + + + maven-compiler-plugin + 3.14.1 + + 25 + 25 + + + + org.apache.maven.plugins + maven-source-plugin + 3.4.0 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.12.0 + + + attach-javadocs + + jar + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.9.0 + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/teren/lib + false + false + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.5.0 + + ${project.build.directory}/teren/lib + + + true + pl.wat.ms4ds.terenfunkcje.nmt.NMTDataProvider + + + teren-funkcje-1.0.2-SNAPSHOT.jar + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.4.0 + + + copy-resources-runbat + validate + + copy-resources + + + ${project.build.directory}/teren/lib + + + ${basedir} + true + + *.properties + *.bat + *.xml + + + + + + + + + + + + + + nexus-snapshots + https://nexus.rulka.pl/repository/maven-snapshots/ + + + nexus-releases + https://nexus.rulka.pl/repository/maven-releases/ + + + + + + ms4ds + common-types + 1.0 + + + + + + + + ch.qos.logback + logback-core + 1.5.20 + + + ch.qos.logback + logback-classic + 1.5.20 + + + org.slf4j + slf4j-api + 2.0.17 + compile + + + org.nocrala + shapefilereader + 1.0 + + + \ No newline at end of file diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/AStar.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/AStar.java new file mode 100644 index 0000000..a23dd52 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/AStar.java @@ -0,0 +1,338 @@ +package pl.wat.ms4ds.terenfunkcje; + +import pl.wat.ms4ds.common.EGeoDirection; +import pl.wat.ms4ds.common.ERodzajDzialania; +import pl.wat.ms4ds.common.ERodzajPodwozia; + +import java.util.*; + +public final class AStar { + + private static class Node { + + int x; + int y; + boolean koncowy; + boolean zamkniety; + double kosztOdStartu; + double kosztZHeurystyka; + Node poprzednik; + EGeoDirection zKierunku; + + private static Hashtable allNodes = new Hashtable<>(); + private static Node roboczy = new Node(0, 0); + + static Node dajAStarNode(int x, int y) { + if (x < 0 || y < 0) { + return null; + } + roboczy.x = x; + roboczy.y = y; + Node node = allNodes.get(roboczy); + if (node == null) { + node = new Node(x, y); + allNodes.put(node, node); + } + return node; + } + + static Node dajAStarNode(GridCoord gridCoord) { + return dajAStarNode(gridCoord.x, gridCoord.y); + } + + ArrayList dajNiezamknietychSasiadow() { + ArrayList wynik = new ArrayList<>(); + Node sasiad; + sasiad = Node.dajAStarNode(this.x - 1, this.y - 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x - 1, this.y); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x - 1, this.y + 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x, this.y - 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x, this.y + 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x + 1, this.y - 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x + 1, this.y); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + sasiad = Node.dajAStarNode(this.x + 1, this.y + 1); + if (null != sasiad && !sasiad.zamkniety) + wynik.add(sasiad); + return wynik; + } + + static void reset() { + allNodes.clear(); + } + + Node(int x, int y) { + this.x = x; + this.y = y; + koncowy = false; + zamkniety = false; + kosztOdStartu = 0.0; + kosztZHeurystyka = Double.MAX_VALUE; + poprzednik = null; + zKierunku = EGeoDirection.UNDEFINED; + } + + Node(GridCoord id) { + this(id.x, id.y); + } + + @Override + public String toString() { + return "AStarNode{" + + "x=" + x + + ", y=" + y + + ", koncowy=" + koncowy + + '}'; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = prime + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof Node other) { + if (x != other.x) { + return false; + } + return y == other.y; + } + return false; + } + } + + /** + * Funkcja wyznacza drogę po sąsiednich kwadratach przechodzącą przez punkty profilujące punktyProfilujace + * + * @param punktyProfilujace punkty profilujące drogę (droga musi je zawierać) + * @param staryKierunek kierunek z którego osiągnięto punkt startowy (1. punkt profilujący) + * @param podwozie rodzaj podwozia + * @param rodzajDzialania rodzaj działania, w ramach którego określana jest droga + * @return uporządkowana kolekcja współrzędnych kolejnych kwadratów drogi zawierająca kwadrat startowy i docelowy lub kolekcja pusta, gdy nie istnieje droga + */ + public static ArrayList wyznaczDroge(GridCoord[] punktyProfilujace, EGeoDirection staryKierunek, + ERodzajPodwozia podwozie, ERodzajDzialania rodzajDzialania) { + if (null == punktyProfilujace || 0 == punktyProfilujace.length) { + return null; + } + Node.reset(); + ArrayList wynik = new ArrayList<>(); + GridCoord start; + GridCoord stop = punktyProfilujace[0]; + ArrayList odcinek; + for (int i = 1; i < punktyProfilujace.length; i++) { + start = stop; + stop = punktyProfilujace[i]; + EGeoDirection staryKier; + if (wynik.size() > 1) { + staryKier = GeomUtils.kierunekDlaSasiada(wynik.get(wynik.size() - 2), wynik.get(wynik.size() - 1)); + } else { + staryKier = staryKierunek; + } + if (i == 1) { + odcinek = wyznaczDroge(start, stop, staryKier, true, podwozie, rodzajDzialania); + } else { + odcinek = wyznaczDroge(start, stop, staryKier, false, podwozie, rodzajDzialania); + } + if (odcinek.size() == 0) { + // gdy nie istnieje droga między danymi punktami profilującymi, to zwracam kolekcję pustą + return odcinek; + } + wynik.addAll(odcinek); + Node.reset(); + } + return wynik; + } + + /** + * Funkcja wyznacza drogę po sąsiednich kwadratach od kwadratu startowego start do docelowego stop + * + * @param start współrzędne kwadratu startowego + * @param stop współrzędne kwadratu docelowego + * @param staryKierunek kierunek z którego osiągnięto punkt startowy (1. punkt profilujący) + * @param podwozie rodzaj podwozia + * @param rodzajDzialania rodzaj działania, w ramach którego określana jest droga + * @param zawieraStartowy parametr wskazujący, czy wyznaczona droga ma zawierać kwadrat startowy + * @return uporządkowana kolekcja współrzędnych kolejnych kwadratów drogi zawierająca kwadrat startowy (jeśli zawieraStartowy==true) i docelowy lub kolekcja pusta, gdy nie istnieje droga + */ + public static ArrayList wyznaczDroge(GridCoord start, GridCoord stop, EGeoDirection staryKierunek, + boolean zawieraStartowy, ERodzajPodwozia podwozie, ERodzajDzialania rodzajDzialania) { + + PriorityQueue listaOtwarta = new PriorityQueue<>(100, (o1, o2) -> Double.compare(o1.kosztZHeurystyka, o2.kosztZHeurystyka)); + boolean naPrzelaj = false; + switch (rodzajDzialania) { + case DZIALANIE_ATAK: + case DZIALANIE_MAGIC_MOVEMENT: + case DZIALANIE_OBRONA: + case DZIALANIE_OPL: + case DZIALANIE_WRIA: + case DZIALANIE_ROZMIESZCZENIE: + case DZIALANIE_ZESRODKOWANIE: + case DZIALANIE_MINOWANIE: + naPrzelaj = true; + break; + default: + break; + + } + Node aktualny = Node.dajAStarNode(stop.x, stop.y); + aktualny.koncowy = true; + ArrayList sasiedzi; + aktualny = Node.dajAStarNode(start.x, start.y); + aktualny.zKierunku = staryKierunek; + listaOtwarta.add(aktualny); + ArrayList wynik = new ArrayList(); + int licznik_zabezpieczajacy = 200000; + while (listaOtwarta.size() > 0 && licznik_zabezpieczajacy-- > 0) { + aktualny = listaOtwarta.remove(); + if (aktualny.koncowy) { + while (null != aktualny) { + wynik.add(new GridCoord(aktualny.x, aktualny.y)); + aktualny = aktualny.poprzednik; + } + if (!zawieraStartowy) { + //Usuwam poczatek drogi + wynik.remove(wynik.size() - 1); + } + Collections.reverse(wynik); + return wynik; + } + sasiedzi = aktualny.dajNiezamknietychSasiadow(); + for (Node sasiad : sasiedzi) { + double stopienPrzejezdnosci = Teren.getStopienPrzejezdnosci(aktualny.x, aktualny.y, sasiad.x, sasiad.y, + aktualny.zKierunku, podwozie); + if (stopienPrzejezdnosci < 0.005f) { + continue; + } + if (naPrzelaj) { + stopienPrzejezdnosci = Math.max(Teren.minStopienPrzejezdNaPrzelaj, stopienPrzejezdnosci); + } + double nowyKosztOdStartu = (float) aktualny.kosztOdStartu + GridCoord.odleglosc(aktualny.x, aktualny.y, sasiad.x, sasiad.y) / stopienPrzejezdnosci; + double nowyKosztZHeurystyka = nowyKosztOdStartu + GridCoord.odleglosc(sasiad.x, sasiad.y, stop.x, stop.y); + if (sasiad.kosztZHeurystyka > nowyKosztZHeurystyka) { + //UPDATE kosztow i zmiany w kolejce + sasiad.kosztOdStartu = nowyKosztOdStartu; + sasiad.kosztZHeurystyka = nowyKosztZHeurystyka; + sasiad.poprzednik = aktualny; + sasiad.zKierunku = GeomUtils.kierunekDlaSasiada(aktualny.x, aktualny.y, sasiad.x, sasiad.y); + listaOtwarta.remove(sasiad); + listaOtwarta.add(sasiad); + } + } + aktualny.zamkniety = true; + } + return wynik; + } + + /** + * Funkcja wyznacza drogę po sąsiednich kwadratach od kwadratu startowego start do docelowego stop + * + * @param start współrzędne kwadratu startowego + * @param stop współrzędne kwadratu docelowego + * @param staryKierunek kierunek z którego osiągnięto punkt startowy (1. punkt profilujący) + * @param podwozie rodzaj podwozia + * @param rodzajDzialania rodzaj działania, w ramach którego określana jest droga + * @param zawieraStartowy parametr wskazujący, czy wyznaczona droga ma zawierać kwadrat startowy + * @return uporządkowana kolekcja współrzędnych kolejnych kwadratów drogi zawierająca kwadrat startowy (jeśli zawieraStartowy==true) i docelowy lub kolekcja pusta, gdy nie istnieje droga + */ + public static ArrayList wyznaczDrogeNew(GridCoord start, GridCoord stop, EGeoDirection staryKierunek, + boolean zawieraStartowy, ERodzajPodwozia podwozie, + ERodzajDzialania rodzajDzialania, + double szerokoscPokonywRowow, + double glebokoscBrodzenia, + double predkoscPlywania) { + PriorityQueue listaOtwarta = new PriorityQueue<>(100, new Comparator() { + public int compare(Node o1, Node o2) { + return Double.compare(o1.kosztZHeurystyka, o2.kosztZHeurystyka); + } + }); +// PriorityQueue listaOtwarta = new PriorityQueue<>(100, (AStarNode o1, AStarNode o2) -> Double.compare(o1.kosztZHeurystyka, o2.kosztZHeurystyka)); + boolean naPrzelaj = false; + switch (rodzajDzialania) { + case DZIALANIE_ATAK: + case DZIALANIE_MAGIC_MOVEMENT: + case DZIALANIE_OBRONA: + case DZIALANIE_OPL: + case DZIALANIE_WRIA: + case DZIALANIE_ROZMIESZCZENIE: + case DZIALANIE_ZESRODKOWANIE: + case DZIALANIE_MINOWANIE: + naPrzelaj = true; + break; + default: + break; + + } + Node aktualny = Node.dajAStarNode(stop.x, stop.y); + aktualny.koncowy = true; + ArrayList sasiedzi; + aktualny = Node.dajAStarNode(start.x, start.y); + aktualny.zKierunku = staryKierunek; + listaOtwarta.add(aktualny); + ArrayList wynik = new ArrayList(); + int licznik_zabezpieczajacy = 200000; + while (listaOtwarta.size() > 0 && licznik_zabezpieczajacy-- > 0) { + aktualny = listaOtwarta.remove(); + if (aktualny.koncowy) { + while (null != aktualny) { + wynik.add(new GridCoord(aktualny.x, aktualny.y)); + aktualny = aktualny.poprzednik; + } + if (!zawieraStartowy) { + //Usuwam poczatek drogi + wynik.remove(wynik.size() - 1); + } + Collections.reverse(wynik); + return wynik; + } + sasiedzi = aktualny.dajNiezamknietychSasiadow(); + for (Node sasiad : sasiedzi) { + double stopienPrzejezdnosci = Teren.getStopienPrzejezdnosciNew(aktualny.x, aktualny.y, sasiad.x, sasiad.y, + aktualny.zKierunku, podwozie, szerokoscPokonywRowow, glebokoscBrodzenia, predkoscPlywania); + if (stopienPrzejezdnosci < 0.005f) { + continue; + } + if (naPrzelaj) { + stopienPrzejezdnosci = Math.max(Teren.minStopienPrzejezdNaPrzelaj, stopienPrzejezdnosci); + } + double nowyKosztOdStartu = (float) aktualny.kosztOdStartu + GridCoord.odleglosc(aktualny.x, aktualny.y, sasiad.x, sasiad.y) / stopienPrzejezdnosci; + double nowyKosztZHeurystyka = nowyKosztOdStartu + GridCoord.odleglosc(sasiad.x, sasiad.y, stop.x, stop.y); + if (sasiad.kosztZHeurystyka > nowyKosztZHeurystyka) { + //UPDATE kosztow i zmiany w kolejce + sasiad.kosztOdStartu = nowyKosztOdStartu; + sasiad.kosztZHeurystyka = nowyKosztZHeurystyka; + sasiad.poprzednik = aktualny; + sasiad.zKierunku = GeomUtils.kierunekDlaSasiada(aktualny.x, aktualny.y, sasiad.x, sasiad.y); + listaOtwarta.remove(sasiad); + listaOtwarta.add(sasiad); + } + } + aktualny.zamkniety = true; + } + return wynik; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/Arkusz.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/Arkusz.java new file mode 100644 index 0000000..eaf6536 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/Arkusz.java @@ -0,0 +1,35 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.util.ArrayList; + +public class Arkusz { + + ArrayList wezly = new ArrayList<>(); + // arkusz mapy zawiera luki rozlacznie (jeden luk moze nalezec tylko do jednego arkusza) + ArrayList luki = new ArrayList<>(); + + // liczba arkuszy mapy + static int lx = 0; + static int ly = 0; + + /** + * Funkcja porownuje wspolrzedne leksykograficznie. + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + static boolean mniejszeWspolrzedne(int x1, int y1, int x2, int y2) { + if (x1 < x2) { + return true; + } + if (x1 > x2) { + return false; + } + if (y1 < y2) { + return true; + } + return false; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/BigSquare.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/BigSquare.java new file mode 100644 index 0000000..b2c273d --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/BigSquare.java @@ -0,0 +1,12 @@ +package pl.wat.ms4ds.terenfunkcje; + +abstract class BigSquare { + abstract Kwadrat getKwadrat(int Id_X, int Id_Y); + + protected transient String fileName; + public int xRefMs = 0; + public int yRefMs = 0; + public transient int liczbaZmian = 0; + // TODO zamienic na 100 + static final int LICZBA_ZMIAN_DO_ZAPISU = 100000; +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/Bresenham.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/Bresenham.java new file mode 100644 index 0000000..902a8ff --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/Bresenham.java @@ -0,0 +1,152 @@ +package pl.wat.ms4ds.terenfunkcje; + + +/** + * 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 GridCoord[] 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; + GridCoord[] res = new GridCoord[dx + 1]; + for (int i = 0; i <= dx; i++) { + res[i] = new GridCoord(x, y); + if (p > 0) { + y += sy; + p -= ddx; + } + x += sx; + p += ddy; + } + return res; + } else { + // poruszamy sie po y + p = ddx - dy; + GridCoord[] res = new GridCoord[dy + 1]; + for (int i = 0; i <= dy; i++) { + res[i] = new GridCoord(x, y); + if (p > 0) { + x += sx; + p -= ddy; + } + y += sy; + p += ddx; + } + return res; + } + } + + + static void print(GridCoord[] 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/terenfunkcje/EmptyBigSquare.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/EmptyBigSquare.java new file mode 100644 index 0000000..6bbff1f --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/EmptyBigSquare.java @@ -0,0 +1,13 @@ +package pl.wat.ms4ds.terenfunkcje; + +class EmptyBigSquare extends BigSquare { + + public static final EmptyBigSquare EMPTY_BIG_SQUARE = new EmptyBigSquare(); + + private EmptyBigSquare() { + } + + Kwadrat getKwadrat(int ssX, int ssY) { + return Kwadrat.EMPTY_SQUARE; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/GeoCoord.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/GeoCoord.java new file mode 100644 index 0000000..ea93f7d --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/GeoCoord.java @@ -0,0 +1,9 @@ +package pl.wat.ms4ds.terenfunkcje; + +/** + * + */ +public class GeoCoord { + public double lat; + public double lon; +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/GeomUtils.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/GeomUtils.java new file mode 100644 index 0000000..3b51144 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/GeomUtils.java @@ -0,0 +1,1647 @@ +package pl.wat.ms4ds.terenfunkcje; + +import pl.wat.ms4ds.common.EGeoDirection; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +public class GeomUtils { + + + /** + *

Wyznacznik macierzy (kwadratowej stopnia 3) + * [[p.x, p.y, 1], + * [q.x, q.y, 1] + * [r.x, r.y, 1]] + *

+ * Pozwala na określenie położenie punktu względem wektora. + * + * @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, + * det3 mniejsze od 0, gdy punkt lezy po prawej stronie wektora, + */ + public static long det3(GridCoord p, GridCoord q, GridCoord r) { + return p.x * (q.y - r.y) + q.x * (r.y - p.y) + r.x * (p.y - q.y); + } + + /** + *

Wyznacznik macierzy (kwadratowej stopnia 3) + * [[p.x, p.y, 1], + * [q.x, q.y, 1] + * [r.x, r.y, 1]] + *

+ * Pozwala na określenie położenie punktu względem wektora. + * + * @param px Wsp x poczatku wektora + * @param py Wsp y poczatku wektora + * @param qx Wsp x końca wektora + * @param qy Wsp y końca wektora + * @param rx Wsp x testowanego punktu + * @param ry Wsp y testowanego punktu + * @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(int px, int py, int qx, int qy, int rx, int ry) { + return px * (qy - ry) + qx * (ry - py) + rx * (py - qy); + } + + /** + * Sprawdza, czy punkt r należy do odcinka p-q + * + * @param p Poczatek odcinka + * @param q Koniec odcinka + * @param r Punkt testowany + * @return true, jesli punkt r lezy na odcinku + */ + public static boolean include(GridCoord p, GridCoord q, GridCoord r) { + return det3(p, q, r) == 0 && + Math.min(p.x, q.x) <= r.x && + r.x <= Math.max(p.x, q.x) && + Math.min(p.y, q.y) <= r.y && + r.y <= Math.max(p.y, q.y); + } + + /** + * Sprawdza, czy punkt r należy do odcinka p-q + * + * @param px Wsp x poczatku wektora + * @param py Wsp y poczatku wektora + * @param qx Wsp x końca wektora + * @param qy Wsp y końca wektora + * @param rx Wsp x testowanego punktu + * @param ry Wsp y testowanego punktu + * @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 && + rx <= Math.max(px, qx) && + Math.min(py, qy) <= ry && + ry <= Math.max(py, qy); + } + + /** + * Sprawdza, czy punkt r należy do poziomego odcinka p-q + * + * @param p Poczatek odcinka + * @param q Koniec odcinka + * @param r Punkt testowany + * @return true, jeśli punkt r należy do odcinka p-q + */ + public static boolean includeHorizontaly(GridCoord p, GridCoord q, GridCoord r) { + return p.y == r.y && + Math.min(p.x, q.x) <= r.x && + r.x <= Math.max(p.x, q.x); + +// return det3(p, q, r) == 0 && +// Math.min(p.x, q.x) <= r.x && +// r.x <= Math.max(p.x, q.x) && +// Math.min(p.y, q.y) <= r.y && +// r.y <= Math.max(p.y, q.y); + } + + /** + * Sprawdza, czy punkt r należy do poziomego odcinka p-q + * + * @param px Wsp x poczatku wektora + * @param py Wsp y poczatku wektora + * @param qx Wsp x końca wektora + * @param qy Wsp y końca wektora + * @param rx Wsp x testowanego punktu + * @param ry Wsp y testowanego punktu + * @return true, jeśli punkt r należy do odcinka p-q + */ + public static boolean includeHorizontaly(int px, int py, int qx, int qy, int rx, int ry) { + return py == ry && + Math.min(px, qx) <= rx && + rx <= Math.max(px, qx); + } + + /** + * Określenie znaku liczby + * + * @param x Testowana liczba + * @return 0, gdy x == 0, 1 gdy x wieksze od 0, -1 gdy x mniejsze od 0 + */ + public static int sgn(long x) { + if (x == 0) { + return 0; + } + if (x > 0) { + return 1; + } + return -1; + } + + /** + * Sprawdza, czy odcinki p-q i r-s przecinają się wewnetrznie tzn. + * mają dokładnie jeden punkt wspólny różny od każdego z konców odcinków. + * + * @param p punkt początkowy pierwszego odcinka + * @param q punkt końcowy pierwszego odcinka + * @param r punkt początkowy drugiego odcinka + * @param s punkt końcowy drugiego odcinka + * @return true, jeśli odcinki przecinają się wewnętrznie + */ + public static boolean cross(GridCoord p, GridCoord q, GridCoord r, GridCoord s) { + long det3_pqr = det3(p, q, r); + long det3_pqs = det3(p, q, s); + long det3_rsp = det3(r, s, p); + long det3_rsq = det3(r, s, q); + + return sgn(det3_pqr) != sgn(det3_pqs) && + sgn(det3_rsp) != sgn(det3_rsq) && + det3_pqr != 0 && + det3_pqs != 0 && + det3_rsp != 0 && + det3_rsq != 0; +// return sgn(det3(p, q, r)) != sgn(det3(p, q, s)) && +// sgn(det3(r, s, p)) != sgn(det3(r, s, q)) && +// !include(p, q, r) && +// !include(p, q, s) && +// !include(r, s, p) && +// !include(r, s, q); + } + + /** + * Sprawdza, czy odcinki p-q i r-s przecinają się wewnetrznie tzn. + * mają dokładnie jeden punkt wspólny różny od każdego z konców odcinków. + * + * @param px wsp x punktu początkowego pierwszego odcinka + * @param py wsp y punktu początkowego pierwszego odcinka + * @param qx wsp x punktu końcowego pierwszego odcinka + * @param qy wsp y punktu końcowego pierwszego odcinka + * @param rx wsp x punktu początkowego drugiego odcinka + * @param ry wsp y punktu początkowego drugiego odcinka + * @param sx wsp x punktu końcowego drugiego odcinka + * @param sy wsp y punktu końcowego drugiego odcinka + * @return true, jeśli odcinki przecinają się wewnętrznie + */ + public static boolean cross(int px, int py, int qx, int qy, int rx, int ry, int sx, int sy) { + long det3_pqr = det3(px, py, qx, qy, rx, ry); + long det3_pqs = det3(px, py, qx, qy, sx, sy); + long det3_rsp = det3(rx, ry, sx, sy, px, py); + long det3_rsq = det3(rx, ry, sx, sy, qx, qy); + + return sgn(det3_pqr) != sgn(det3_pqs) && + sgn(det3_rsp) != sgn(det3_rsq) && + det3_pqr != 0 && + det3_pqs != 0 && + det3_rsp != 0 && + det3_rsq != 0; +// return sgn(det3(p, q, r)) != sgn(det3(p, q, s)) && +// sgn(det3(r, s, p)) != sgn(det3(r, s, q)) && +// !include(p, q, r) && +// !include(p, q, s) && +// !include(r, s, p) && +// !include(r, s, q); + } + + /** + * Sprawdza, czy odcinki p-q i r-s mają dowolną część wspólną. + * + * @param p punkt początkowy pierwszego odcinka + * @param q punkt końcowy pierwszego odcinka + * @param r punkt początkowy drugiego odcinka + * @param s punkt końcowy drugiego odcinka + * @return true, jeśli odcinki mają część wspólną + */ + public static boolean intersection(GridCoord p, GridCoord q, GridCoord r, GridCoord s) { + long det3_pqr = det3(p, q, r); + long det3_pqs = det3(p, q, s); + long det3_rsp = det3(r, s, p); + long det3_rsq = det3(r, s, q); + return (sgn(det3_pqr) != sgn(det3_pqs) && sgn(det3_rsp) != sgn(det3_rsq)) || + (det3_pqr == 0 && det3_pqs == 0 && + ((Math.min(p.x, q.x) <= r.x && + r.x <= Math.max(p.x, q.x) && + Math.min(p.y, q.y) <= r.y && + r.y <= Math.max(p.y, q.y)) || + (Math.min(p.x, q.x) <= s.x && + s.x <= Math.max(p.x, q.x) && + Math.min(p.y, q.y) <= s.y && + s.y <= Math.max(p.y, q.y)))); + } + + /** + * Sprawdza, czy punkt należy do wielokata w ogólnym przypadku (boki wielokata bez samoprzecięć). + * + * @param polygon Kolejne wierzchołki wielokata. + * @param p Testowany punkt. + * @return true, jeśli testowany punkt leży wewnątrz wielokąta + */ + public static boolean insidePolygon(GridCoord[] polygon, GridCoord 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; + } + } + // 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 + GridCoord p1 = new GridCoord(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; + } + + /** + * Sprawdza, czy punkt należy do wielokata w ogólnym przypadku (boki wielokata bez samoprzecięć). + * + * @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 + */ + public static boolean insidePolygon(GridCoord[] polygon, int px, int py) { + // sprawdzenie czy punkt p nie 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].x, polygon[i].y, polygon[i_plus_1].x, polygon[i_plus_1].y, px, py)) { + 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 + GridCoord p1 = new GridCoord(px + 1, py); + 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++; + } + // 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++; + } + } + } 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; + } + + /** + * Wyznacza kierunek przeciwny dla zadanego, przy założeniu, że kierunki geograficzne numerowane są od 0 do 7 + * + * @param kierunek zadany kierunek + * @return kierunek przeciwny dla zadanego + */ + public static int kierunekPrzeciwny(int kierunek) { + int kierPrzeciwny = (kierunek + 4) % 8; + return kierPrzeciwny; + } + + // tangens kąta alfa = 22.5 stopnia + static final double TANG_22_5_DEG = Math.tan(Math.PI / 8); + // tangens kąta alfa = 22.5 stopnia + static final double TANG_67_5_DEG = Math.tan(3 * Math.PI / 8); + + /** + * Wyznacza najbliższy kierunek geograficzny dla zadanego wektora. + * + * @param start kwadrat początkowy wektora + * @param stop kwadrat końcowy wektora + * @return kierunek geograficzny klasy EGeoDirection + */ + public static EGeoDirection kierunek(GridCoord start, GridCoord stop) { + if (start.x == stop.x && start.y == stop.y) { + return EGeoDirection.UNDEFINED; + } + // polnoc-poludnie + if (start.x == stop.x) { + if (start.y < stop.y) { + return EGeoDirection.NORTH; //na polnoc OK + } else { + // Start.Y >= Stop.Y + return EGeoDirection.SOUTH; //na poludnie OK + } + } + // wschod-zachod + if (start.y == stop.y) { + if (start.x < stop.x) { + return EGeoDirection.EAST; //na wschod OK + } else { + // Start.X >= Stop.X + return EGeoDirection.WEST; //na zachod OK + } + } + // pozostale kierunki + double dx = stop.x - start.x; + double dy = stop.y - start.y; + if (dx > 0) { + double tgAlfa = dy / dx; + if (tgAlfa >= TANG_67_5_DEG) { + return EGeoDirection.NORTH; + } else if (tgAlfa >= TANG_22_5_DEG) { + return EGeoDirection.NORTHEAST; + } else if (tgAlfa >= -TANG_22_5_DEG) { + return EGeoDirection.EAST; + } else if (tgAlfa >= -TANG_67_5_DEG) { + return EGeoDirection.SOUTHEAST; + } else { + return EGeoDirection.SOUTH; + } + } else { + double tgAlfa = dy / -dx; + if (tgAlfa >= TANG_67_5_DEG) { + return EGeoDirection.NORTH; + } else if (tgAlfa >= TANG_22_5_DEG) { + return EGeoDirection.NORTHWEST; + } else if (tgAlfa >= -TANG_22_5_DEG) { + return EGeoDirection.WEST; + } else if (tgAlfa >= -TANG_67_5_DEG) { + return EGeoDirection.SOUTHWEST; + } else { + return EGeoDirection.SOUTH; + } + } + } + + /** + * Funkcja zwraca kierunek geograficzny dla wektora opisanego na sasiednich kwadratach. + * + * @param start współrzędne (X, Y) siatki kwadratu początkowego + * @param stop współrzędne (X, Y) siatki kwadratu końcowego + * @return kierunek geograficzny klasy EGeoDirection dla wektora zadanego przez kwadrat początkowy + * i końcowy lub UNDEFINED dla kwadratów niesąsiednich + */ + public static EGeoDirection kierunekDlaSasiada(GridCoord start, GridCoord stop) { + int d = Math.abs(start.x - stop.x) + Math.abs(start.y - stop.y); + if (d == 1 || d == 2) { + if (start.x == stop.x) { + if (start.y < stop.y) { + return EGeoDirection.NORTH; + } + return EGeoDirection.SOUTH; + } + if (start.y == stop.y) { + if (start.x < stop.x) { + return EGeoDirection.EAST; + } + return EGeoDirection.WEST; + } + if (start.x < stop.x) { + if (start.y < stop.y) { + return EGeoDirection.NORTHEAST; + } + return EGeoDirection.SOUTHEAST; + } + if (start.y < stop.y) { + return EGeoDirection.NORTHWEST; + } + return EGeoDirection.SOUTHWEST; + } else { + return EGeoDirection.UNDEFINED; + } + } + + /** + * Funkcja zwraca kierunek geograficzny dla wektora opisanego na sasiednich kwadratach. + * + * @param xStart współrzędne X siatki kwadratu początkowego + * @param yStart współrzędne Y siatki kwadratu początkowego + * @param xStop współrzędne X siatki kwadratu końcowego + * @param yStop współrzędne Y siatki kwadratu końcowego + * @return kierunek geograficzny dla wektora zadanego przez kwadrat początkowy i końcowy lub UNDEFINED dla kwadratów niesąsiednich + */ + public static EGeoDirection kierunekDlaSasiada(int xStart, int yStart, int xStop, int yStop) { + int d = Math.abs(xStart - xStop) + Math.abs(yStart - yStop); + if (d == 1 || d == 2) { + if (xStart == xStop) { + if (yStart < yStop) { + return EGeoDirection.NORTH; + } + return EGeoDirection.SOUTH; + } + if (yStart == yStop) { + if (xStart < xStop) { + return EGeoDirection.EAST; + } + return EGeoDirection.WEST; + } + if (xStart < xStop) { + if (yStart < yStop) { + return EGeoDirection.NORTHEAST; + } + return EGeoDirection.SOUTHEAST; + } + if (yStart < yStop) { + return EGeoDirection.NORTHWEST; + } + return EGeoDirection.SOUTHWEST; + } else { + return EGeoDirection.UNDEFINED; + } + } + + private static final float PRZEKATNA_MK = MapConsts.DL_MK * (float) Math.sqrt(2); + + /** + * Funkcja wyznacza odległość między środkami kwadratów sąsiednich na zadanym kierunku + * + * @param kierunek zadany kierunek sąsiedztwa + * @return odległość między środkami kwadratów sąsiednich [m] + */ + public static float odlegloscKwadratowSasiednich(EGeoDirection kierunek) { + switch (kierunek) { + case NORTH: + case SOUTH: + case EAST: + case WEST: + return MapConsts.DL_MK; + case NORTHEAST: + case NORTHWEST: + case SOUTHEAST: + case SOUTHWEST: + return PRZEKATNA_MK; + default: + return 0.0f; + } + } + + /** + * Funkcja wyznacza sumaryczną długość drogi podanej jako ciąg sąsiednich kwadratów + * + * @param kwadraty droga jako ciąg sąsiednich kwadratów + * @return sumaryczna długość drogi [m] + */ + public static float dlugoscDrogiPoKwadratch(GridCoord[] kwadraty) { + float dl = 0; + for (int i = 1; i < kwadraty.length; i++) { + dl += GridCoord.odleglosc(kwadraty[i - 1], kwadraty[i]); + } + return dl; + } + + /** + * Funkcja wyznacza sumaryczną długość drogi podanej jako ciąg sąsiednich kwadratów + * + * @param kwadraty droga jako ciąg sąsiednich kwadratów + * @return sumaryczna długość drogi [m] + */ + public static float dlugoscDrogiPoKwadratch(ArrayList kwadraty) { + float dl = 0; + for (int i = 1; i < kwadraty.size(); i++) { + dl += GridCoord.odleglosc(kwadraty.get(i - 1), kwadraty.get(i)); + } + return dl; + } + + // funkcja zwraca kwadrat znajdujacy sie na kierunku + // od kwadratu "kp" do kwadratu "kk" + // w odleglosci "LKwadrat" liczby kwadratow + // LKWadrat nie moze byc wieksze niz odleglosc miedzy kp i kk + public static GridCoord kwadratNaKierunkuWOdleglosci(GridCoord kp, + GridCoord kk, int LKwadrat) { + GridCoord id = null; + GridCoord[] tab = null; + + tab = kwadratyOdcinka(kp.x, kp.y, kk.x, kk.y); + if ((tab != null) && (tab.length > 0)) { + id = new GridCoord(); + if ((LKwadrat < tab.length) && LKwadrat >= 0) { + id.x = tab[LKwadrat].x; + id.y = tab[LKwadrat].y; + } else { + id.x = tab[tab.length - 1].x; + id.y = tab[tab.length - 1].y; + } + } + return id; + } + + /** + * Transformuje wspolrzedne koncow odcinka do odcinka zaczepionego w punkcie (0,0). + * + * @param pocz + * @param kon + * @param kon_nowy + */ + public static void transformOdcinekDo0X0Y(GridCoord pocz, GridCoord kon, + GridCoord kon_nowy) { + if ((pocz == null) || (kon == null)) { + return; + } + if (kon_nowy == null) { + kon_nowy = new GridCoord(); + } + kon_nowy.x = kon.x - pocz.x; + kon_nowy.y = kon.y - pocz.y; + } + + // transformuje wspolrzedne kwadratow na odcinku: kwadraty + // przesuniecie o wektor o wspolrzednych wsp0X0Y + public static void transformKwadratyZ0X0Y(GridCoord wsp0X0Y, + GridCoord[] kwadraty) { + if ((kwadraty == null) || (wsp0X0Y == null)) { + return; + } + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i].x += wsp0X0Y.x; + kwadraty[i].y += wsp0X0Y.y; + } + } + + /** + * Funkcja wyznacza tablicę kwadratów leżących na łamanej. + * + * @param punktyLamanej tablica wspolrzednych tworzących łamaną + * @return kwadratyWyj kolekcja kwadratów leżących na łamanej + */ + public static ArrayList kwadratyLamanej(GridCoord[] punktyLamanej) { + ArrayList kwadratyWyj = new ArrayList(); + if (punktyLamanej.length == 2) { + GridCoord[] kwadraty = kwadratyOdcinka(punktyLamanej[0], punktyLamanej[1]); + for (int i = 0; i < kwadraty.length; i++) { + kwadratyWyj.add(kwadraty[i]); + } + return kwadratyWyj; + } + for (int i = 0; i < punktyLamanej.length - 1; i++) { + GridCoord[] 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]); + } + } + return kwadratyWyj; + } + + /** + * Funkcja wyznacza tablicę kwadratów leżących na łamanej. + * + * @param punktyLamanej tablica wspolrzednych odcinków tworzących łamaną + * @return tablica (ciąg) sąsiednich kwadratów leżących na łamanej + */ + public static GridCoord[] kwadratyLamanej2(GridCoord[] punktyLamanej) { + GridCoord[] kwadratyWyj; + if (punktyLamanej.length == 2) { + kwadratyWyj = kwadratyOdcinka(punktyLamanej[0], punktyLamanej[1]); + return kwadratyWyj; + } + 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; + } + GridCoord[] helpTab = new GridCoord[maxLen]; + int lastLen = 0; + for (int i = 0; i < punktyLamanej.length - 1; i++) { + GridCoord[] 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; + } 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 GridCoord[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 GridCoord[] kwadratyOdcinka(GridCoord pocz, GridCoord 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 GridCoord[] kwadratyOdcinka(int xp, int yp, int xk, int yk) { + GridCoord[] kwadraty; + if ((xp == xk) && (yp == yk)) { + // odcinek skladajacy sie z jednego kwadratu + kwadraty = new GridCoord[1]; + kwadraty[0] = new GridCoord(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 GridCoord[] kwadratyOdcinkaPionowego(int xp, int yp, int xk, int yk) { + GridCoord[] kwadraty = new GridCoord[Math.abs(yp - yk) + 1]; + if (yp < yk) { + // zorientowanie wektora do gory + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(xp, yp + i); + } + } else { + // zorientowanie wektora do dolu + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(xp, yp - i); + } + } + 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 GridCoord[] kwadratyOdcinkaPoziomego(int xp, int yp, int xk, int yk) { + GridCoord[] kwadraty = new GridCoord[Math.abs(xp - xk) + 1]; + if (xp < xk) { + // zorientowanie wektora w prawo + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(xp + i, yp); + } + } else { + // zorientowanie wektora w lewo + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(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 GridCoord[] kwadratyOdcinkaDowolnego(int xp, int yp, int xk, int yk) { + int x = xp; + int y = yp; + int dx = xk - xp; + int dy = yk - yp; + GridCoord[] 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 GridCoord[abs_dx + 1]; + int i = 0; + tab[i] = new GridCoord(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 GridCoord(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 GridCoord(x, y); + } + } + } 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 GridCoord(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 GridCoord(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; + } + } + return tab; + } + + /** + * Funkcja pomocnicza. + * Zwraca kwadraty lezace na odcinku zorientowanaym pod dowolnym katem wzgledem osi OX. + * + * @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 GridCoord[] 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.DL_MK + MapConsts.DL_MK / 2; + int yyp = yp * MapConsts.DL_MK + MapConsts.DL_MK / 2; + b = yyp - a * xxp; + + if (dx > 0) { + // idziemy w prawo w kierunku rosnacych wartosci x + GridCoord[] temp = new GridCoord[Math.abs(dx) + Math.abs(dy) + 1]; + int dl = 0; + temp[dl] = new GridCoord(); + 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.DL_MK; + xg = x * MapConsts.DL_MK; + float ydf = a * xd + b; + float ygf = a * xg + b; + yd = (int) ydf; + yg = (int) ygf; + y1 = yd / MapConsts.DL_MK + 1; + y2 = yg / MapConsts.DL_MK + 1; + temp[dl] = new GridCoord(); + temp[dl].x = x; + temp[dl].y = y1; + dl++; + if (y1 != y2) { + temp[dl] = new GridCoord(); + temp[dl].x = x; + temp[dl].y = y2; + dl++; + } + } + temp[dl] = new GridCoord(); + temp[dl].x = xk; + temp[dl].y = yk; + dl++; + GridCoord[] kwadraty = new GridCoord[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 + GridCoord[] temp = kwadratyOdcinkaDowolnego2(xk, yk, xp, yp); + GridCoord[] kwadraty = new GridCoord[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 + GridCoord[] 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; + } + } + + /** + * Funkcja pomocnicza. + * Zwraca kwadraty lezace na odcinku zorientowanaym pod katem 45 lub 135 stop. wzgledem osi OX. + * + * @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 GridCoord[] kwadratyOdcinkaNaPrzekatnej(int xp, int yp, int xk, int yk) { + int dx = xk - xp; + int dy = yk - yp; + GridCoord[] kwadraty = new GridCoord[Math.abs(dx) + 1]; + if ((dx > 0) && (dy > 0)) { + // wektor typu "/" do gory + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(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 GridCoord(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 GridCoord(xp - i, yp - i); + } + } else { + // wektor typu "\" do gory + for (int i = 0; i < kwadraty.length; i++) { + kwadraty[i] = new GridCoord(xp - i, yp + i); + } + } + return kwadraty; + } + + /** + * Funkcja wyznacza ograniczenie dolne wspolrzednych rejonu + * + * @param rejon + * @return ograniczenie dolne wspolrzednych rejonu + */ + public static GridCoord wspXYOgranDolne(GridCoord[] rejon) { + if (rejon == null) { + return null; + } + GridCoord kw_min = new GridCoord(); + 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; + } + } + return kw_min; + } + + /** + * Funkcja wyznacza ograniczenie górne wspolrzednych rejonu + * + * @param rejon + * @return ograniczenie górne wspolrzednych rejonu + */ + public static GridCoord wspXYOgranGorne(GridCoord[] rejon) { + if (rejon == null) { + return null; + } + GridCoord kw_max = new GridCoord(); + 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 GridCoord[] poprawRejon(GridCoord[] rejon) { + if ((rejon == null) || (rejon.length <= 0)) { + return null; + } + int j = 0; + GridCoord[] rejon_temp = new GridCoord[rejon.length]; + rejon_temp[j] = rejon[j]; + for (int i = j + 1; i < rejon.length; i++) { + if (!GridCoord.czyIdentyczne(rejon[i], rejon_temp[j])) { + if (i == rejon.length - 1) { + if (GridCoord.czyIdentyczne(rejon[i], rejon_temp[0])) { + break; + } + } + j++; + rejon_temp[j] = rejon[i]; + } + } + if ((j > 0) && (GridCoord.czyIdentyczne(rejon_temp[0], rejon_temp[j]))) { + j--; + } + GridCoord[] rejon_popr = new GridCoord[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 GridCoord[] kwadratyObszaru(GridCoord[] rej) { + if (rej == null) { + return null; + } + GridCoord[] kwadraty = null; + GridCoord[] rejon = poprawRejon(rej); + if (rejon.length == 1) { //TODO Tu raczej jest blad + kwadraty = new GridCoord[1]; + return kwadraty; + } + if (rejon.length == 2) { + return kwadratyOdcinka(rejon[0].x, rejon[0].y, rejon[1].x, rejon[1].y); + } + GridCoord kw_min = wspXYOgranDolne(rejon); + GridCoord kw_max = wspXYOgranGorne(rejon); + int dl = (kw_max.x - kw_min.x + 1) * (kw_max.y - kw_min.y + 1); + GridCoord[] kwadraty_temp = new GridCoord[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 GridCoord(x, y); + dl++; + } + } + } + kwadraty = new GridCoord[dl]; + System.arraycopy(kwadraty_temp, 0, kwadraty, 0, dl); +// for (int i = 0; i < kwadraty.length; i++) { +// kwadraty[i] = new GridCoord(); +// kwadraty[i].x = kwadraty_temp[i].x; +// kwadraty[i].y = kwadraty_temp[i].y; +// } + return kwadraty; + } + + /** + * Funkcja wyznacza srodek zadanego odcinka + * + * @param odc odcinek wejściowy + * @return współrzędne środka zadanego odcinka + */ + public static GridCoord srodekOdcinka(GridCoord[] odc) { + GridCoord srodek = null; + int xsr = 0, ysr = 0; + if ((odc != null) && (odc.length > 0)) { + srodek = new GridCoord(); + 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; + } + 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 GridCoord srodekOdcinka(GridCoord pocz, GridCoord kon) { + GridCoord 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 GridCoord(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 GridCoord srodekOdcinkaWazony(GridCoord pocz, + GridCoord kon, float waga) { + GridCoord 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 GridCoord(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; + } + float dl = 0.0f; + GridCoord idkw1 = droga.get(pocz); + GridCoord idkw2; + for (int j = pocz + 1; j < droga.size() - 1; j++) { + idkw2 = droga.get(j); + dl += GridCoord.odleglosc(idkw1.x, idkw1.y, idkw2.x, idkw2.y); + idkw1 = idkw2; + } + return 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(GridCoord p1, GridCoord 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.DL_MK; + return odl; + } + + /** + * Funkcja zwraca odległość między środkami małych kwadratów [m]. + * + * @param x1 Wsp X id malego kwadratu pocz. + * @param y1 Wsp Y id malego kwadratu pocz. + * @param x2 Wsp X id malego kwadratu konc. + * @param y2 Wsp Y id malego kwadratu konc. + * @return Odległość miedzy srodkami malych kwadratow [m]. + */ + public static float odleglosc(int x1, int y1, int x2, int y2) { + // odleglosc miedzy srodkami malych kwadratow + int xx = x1 - x2; + xx *= xx; + int yy = y1 - y2; + yy *= yy; + float odl = (float) Math.sqrt(xx + yy); + odl *= MapConsts.DL_MK; + 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.DL_MK; + return odl; + } + + // ===================================================================== + + public static void main(String[] args) { + float odl = GridCoord.odleglosc(1, 1, 2, 2); + float odl2 = GridCoord.odlegloscApprox(1, 1, 2, 2); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + odl = GridCoord.odleglosc(1, 1, 21, 21); + odl2 = GridCoord.odlegloscApprox(1, 1, 21, 21); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + odl = GridCoord.odleglosc(1, 1, 1, 21); + odl2 = GridCoord.odlegloscApprox(1, 1, 1, 21); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + odl = GridCoord.odleglosc(1, 1, 10, 21); + odl2 = GridCoord.odlegloscApprox(1, 1, 10, 21); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + odl = GridCoord.odleglosc(1, 1, 100, 210); + odl2 = GridCoord.odlegloscApprox(1, 1, 100, 210); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + odl = GridCoord.odleglosc(1, 1, 5, 210); + odl2 = GridCoord.odlegloscApprox(1, 1, 5, 210); + System.out.print("dist = " + odl + ", DELTA = " + (odl - odl2)); + System.out.println(", delta = " + (odl - odl2) / odl); + + GridCoord p = new GridCoord(4, 6); + GridCoord q = new GridCoord(8, 2); + GridCoord r = new GridCoord(6, 4); + GridCoord s = new GridCoord(8, 6); + + GridCoord startCoord = new GridCoord(10, 10); + GridCoord stopCoord = new GridCoord(); + for (int y = 20; y >= 0; y--) { + for (int x = 0; x <= 20; x++) { + stopCoord.set(x, y); + EGeoDirection kier = kierunek(startCoord, stopCoord); + switch (kier) { + case NORTH: + System.out.print('|'); + break; + case NORTHEAST: + System.out.print('/'); + break; + case EAST: + System.out.print('-'); + break; + case SOUTHEAST: + System.out.print('\\'); + break; + case SOUTH: + System.out.print('|'); + break; + case SOUTHWEST: + System.out.print('/'); + break; + case WEST: + System.out.print('-'); + break; + case NORTHWEST: + System.out.print('\\'); + break; + case UNDEFINED: + System.out.print('O'); + break; + } + } + System.out.println(); + } + System.out.println(); + + EGeoDirection kier = kierunek(new GridCoord(10, 10), new GridCoord(10, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(10, 5)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 10)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 10)); + System.out.println(kier); + + kier = kierunek(new GridCoord(10, 10), new GridCoord(9, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(11, 15)); + System.out.println(kier); + + kier = kierunek(new GridCoord(10, 10), new GridCoord(9, 5)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(11, 5)); + System.out.println(kier); + + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 9)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 11)); + System.out.println(kier); + + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 9)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 11)); + System.out.println(kier); + + + // Kierunki pośrednie: PLN-WSC, PLD-WSC, PLN-ZAC, PLD-ZAC + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 5)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 5)); + System.out.println(kier); + + // PLN-WSC + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 16)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 14)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(16, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(14, 15)); + System.out.println(kier); + + // PLD-WSC + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 6)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(15, 4)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(16, 5)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(14, 5)); + System.out.println(kier); + + //PLN-ZAC + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 16)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 14)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(6, 15)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(4, 15)); + System.out.println(kier); + + // PLD-ZAC + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 6)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(5, 4)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(6, 5)); + System.out.println(kier); + kier = kierunek(new GridCoord(10, 10), new GridCoord(4, 5)); + System.out.println(kier); + + boolean przeciecie = intersection(p, q, r, s); + r.set(8, 2); + s.set(11, 5); + przeciecie = intersection(p, q, r, s); + r.set(6, 4); + s.set(7, 3); + przeciecie = intersection(p, q, r, s); + r.set(6, 4); + s.set(10, 0); + przeciecie = intersection(p, q, r, s); + r.set(4, 4); + s.set(6, 6); + przeciecie = intersection(p, q, r, s); + r.set(4, 8); + s.set(6, 10); + przeciecie = intersection(p, q, r, s); + r.set(2, 8); + s.set(1, 9); + przeciecie = intersection(p, q, r, s); + + + GridCoord[] lamana = {new GridCoord(8, 8), new GridCoord(3, 4), new GridCoord(6, 11)}; + GridCoord[] tab = kwadratyLamanej2(lamana); + + + long det = det3(1, 0, 4, 0, 2, 2); + det = det3(1, 0, 4, 0, 2, -2); + + det = det3(1, 1, 1, 4, 0, 2); + det = det3(1, 1, 1, 4, 2, 2); + + det = det3(1, 1, 4, 4, 4, 3); + det = det3(1, 1, 4, 4, 3, 4); + + +// GridCoord[] polygon = new GridCoord[21]; +// polygon[0] = new GridCoord(12, 18); +// polygon[1] = new GridCoord(16, 18); +// polygon[2] = new GridCoord(18, 13); +// polygon[3] = new GridCoord(21, 13); +// polygon[4] = new GridCoord(22, 9); +// polygon[5] = new GridCoord(20, 7); +// polygon[6] = new GridCoord(17, 7); +// polygon[7] = new GridCoord(15, 9); +// polygon[8] = new GridCoord(13, 7); +// polygon[9] = new GridCoord(15, 5); +// polygon[10] = new GridCoord(17, 5); +// polygon[11] = new GridCoord(14, 2); +// polygon[12] = new GridCoord(8, 8); +// polygon[13] = new GridCoord(9, 3); +// polygon[14] = new GridCoord(6, 4); +// polygon[15] = new GridCoord(3, 4); +// polygon[16] = new GridCoord(2, 10); +// polygon[17] = new GridCoord(6, 11); +// polygon[18] = new GridCoord(6, 13); +// polygon[19] = new GridCoord(7, 15); +// polygon[20] = new GridCoord(10, 13); +// +// +// // przesunięcie wielokata o wektor +// for (int k = 0; k < polygon.length; k++) { +// polygon[k].x += 1000; +// polygon[k].y += 1000; +// } +// GridCoord p = new GridCoord(); +// for (int j = 20; j >= 0; j--) { +// for (int i = 0; i < 25; i++) { +// p.x = i + 1000; +// p.y = j + 1000; +// char c = ' '; +// if (insidePolygon(polygon, p)) { +// c = 'O'; +// } +// for (int k = 0; k < polygon.length; k++) { +// if (polygon[k].equals(p)) { +// c = 'X'; +// break; +// } +// } +// System.out.print(c); +// System.out.print(' '); +// } +// System.out.println(); +// } +// int lp = args.length / 2; +// for (int i = 0; i < lp; i++) { +// p.x = Integer.parseInt(args[2 * i]); +// p.y = Integer.parseInt(args[2 * i + 1]); +// if (insidePolygon(polygon, p)) { +// System.out.println("Punkt " + p + " leży wewnatrz"); +// } else { +// System.out.println("Punkt " + p + " leży na zewnatrz"); +// } +// } +// float odl = odleglosc(5000, 4000, 2, 2); +// odl = odleglosc(1, 1000, 3999, 3); +// odl = odleglosc(5, 1, 27, 11); +// odl = odleglosc(10, 1, 2, 20); +// odl = odleglosc(1, 1, 2, 2); +// +// GridCoord[] rejon = new GridCoord[4]; +// rejon[0] = new GridCoord(20, 18); +// rejon[1] = new GridCoord(27, 9); +// rejon[2] = new GridCoord(9, 1); +// rejon[3] = new GridCoord(2, 15); +// +// ArrayList kwadraty = kwadratyLamanej(rejon); +// System.out.println(kwadraty); + + +// try { +// BufferedWriter wyj = new BufferedWriter(new FileWriter("A.txt")); + // if (args.length == 5 ) { + // int xp = Integer.parseInt(args[1]); + // int yp = Integer.parseInt(args[2]); + // int xk = Integer.parseInt(args[3]); + // int yk = Integer.parseInt(args[4]); + + /* + * int xp = 50000; int yp = 8000; int xk = 20; int yk = 1; + */ +// GridCoord[] rejon = new GridCoord[4]; +// rejon[0] = new GridCoord(); +// rejon[0].x = 20; +// rejon[0].y = 18; +// rejon[1] = new GridCoord(); +// rejon[1].x = 27; +// rejon[1].y = 9; +// rejon[2] = new GridCoord(); +// rejon[2].x = 9; +// rejon[2].y = 1; +// rejon[3] = new GridCoord(); +// rejon[3].x = 2; +// rejon[3].y = 15; +// GridCoord[] kwadraty = kwadratyObszaru(rejon); + + /* + * WspXY[] kwadraty = kwadratyOdcinka(xp, yp, xk, yk); + * wyj.write("pocz: ("); wyj.write(Integer.toString(xp)); + * wyj.write(", "); wyj.write(Integer.toString(yp)); wyj.write(")"); + * wyj.newLine(); wyj.write("kon: ("); + * wyj.write(Integer.toString(xk)); wyj.write(", "); + * wyj.write(Integer.toString(yk)); wyj.write(")"); wyj.newLine(); + */ +// wyj.write(Integer.toString(kwadraty.length)); +// wyj.newLine(); +// for (int i = 0; i < kwadraty.length; i++) { +// wyj.write("("); +// wyj.write(Integer.toString(kwadraty[i].x)); +// wyj.write(", "); +// wyj.write(Integer.toString(kwadraty[i].y)); +// wyj.write(")"); +// wyj.newLine(); +// } +// +// wyj.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/GridCoord.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/GridCoord.java new file mode 100644 index 0000000..6268c0e --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/GridCoord.java @@ -0,0 +1,689 @@ +package pl.wat.ms4ds.terenfunkcje; + +//import pl.wat.wcy.exception.IdKwadratuException; + + +import pl.wat.ms4ds.common.EGeoDirection; + +public class GridCoord { + + public int x; + public int y; + + /** + * Konstruktor klasy na bazie współrzędnych geograficznych. + * + * @param lon długość geograficzna + * @param lat szerokość geograficzna + */ + public GridCoord(double lon, double lat) { + double xms_f = (lon + 180) * MapConsts.DEG_MS; + long xms = (long) xms_f; + x = zamienWspXmsNaIdKwadratuX(xms); + + double yms_f = (lat + 90) * MapConsts.DEG_MS; + long yms = (long) yms_f; + y = zamienWspYmsNaIdKwadratuY(yms); + } + + /** + * Funkcja zamienia dlugosc geog. xms w milisekundach na IdKwadrat.x. + * + * @param xms długość geograficzna (lon) w milisekundach + * @return współrzędna GridCoord.x + */ + public static int zamienWspXmsNaIdKwadratuX(long xms) { + // wspolrzedne geograficzne w milisekundach zawieraja sie w zakresie: + // 0 <= x < 360 dlugosc geograficzna + // 0 <= y <= 180 szerokosc geograficzna + if ((xms < 0) || (xms >= 360 * MapConsts.DEG_MS)) { + // poza zakresem + return -1; + } + long x = xms; + if (x < MapConsts.X_REF_MS) { +// // poza zakresem + long dx = x + MapConsts.ANGLE_360_MS - MapConsts.X_REF_MS; + if (dx > MapConsts.DX_REF_MS) { + return -1; + } + x += MapConsts.ANGLE_360_MS; + } +// if (x > MapConsts.X_REF_MS + MapConsts.DX_REF_MS) { +// // poza zakresem +// return -1; +// } + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + double xx = (x - MapConsts.X_REF_MS) * ODWROT_SS_DX_MS; +// x = (x - MapConsts.X_REF_MS) / MapConsts.SS_DX_MS; + return (int) xx; + } + + /** + * Funkcja zamienia szerokosc geog. yms w milisekundach na IdKwadrat.y. + * + * @param yms szerokosc geograficzna (lat) w milisekundach + * @return współrzędna GridCoord.y + */ + public static int zamienWspYmsNaIdKwadratuY(long yms) { + // wspolrzedne geograficzne w milisekundach zawieraja sie w zakresie: + // 0 <= x < 360 dlugosc geograficzna + // 0 <= y <= 180 szerokosc geograficzna +// if ((yms < MapConsts.Y_REF_MS) || (yms > MapConsts.Y_REF_MS + MapConsts.DY_REF_MS)) { +// // poza zakresem +// return -1; +// } + if (yms < MapConsts.Y_REF_MS) { + // poza zakresem + return -1; + } + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + double yy = (yms - MapConsts.Y_REF_MS) * ODWROT_SS_DY_MS; +// long y = (yms - MapConsts.Y_REF_MS) / MapConsts.SS_DY_MS; + return (int) yy; + } + + /** + * Zamienia długość geograficzną na współrzędna x GridCoord. + * + * @param lon długość geograficzna + * @return współrzędna x klasy GridCoord + */ + public static int zamienDlugoscGeoNaIdKwadratuX(double lon) { + double xms_f = (lon + 180) * MapConsts.DEG_MS; + return zamienWspXmsNaIdKwadratuX((long) xms_f); + } + + /** + * Zamienia szerokość geograficzną na współrzędna y GridCoord. + * + * @param lat szerokość geograficzna + * @return współrzędna y klasy GridCoord + */ + public static int zamienSzerokoscGeoNaIdKwadratuY(double lat) { + double yms_f = (lat + 90) * MapConsts.DEG_MS; + return zamienWspYmsNaIdKwadratuY((long) yms_f); + } + + public GridCoord kwadratSasiedni(EGeoDirection kier) { + GridCoord idKwSasiedni = new GridCoord(); + switch (kier) { + case NORTH: + idKwSasiedni.x = x; + idKwSasiedni.y = y + 1; + break; + case NORTHEAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y + 1; + break; + case EAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y; + break; + case SOUTHEAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y - 1; + break; + case SOUTH: + idKwSasiedni.x = x; + idKwSasiedni.y = y - 1; + break; + case SOUTHWEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y - 1; + break; + case WEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y; + break; + case NORTHWEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y + 1; + break; + default: + } + return idKwSasiedni; + } + + public void kwadratSasiedni(GridCoord idKwSasiedni, EGeoDirection kier) { + switch (kier) { + case NORTH: + idKwSasiedni.x = x; + idKwSasiedni.y = y + 1; + break; + case NORTHEAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y + 1; + break; + case EAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y; + break; + case SOUTHEAST: + idKwSasiedni.x = x + 1; + idKwSasiedni.y = y - 1; + break; + case SOUTH: + idKwSasiedni.x = x; + idKwSasiedni.y = y - 1; + break; + case SOUTHWEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y - 1; + break; + case WEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y; + break; + case NORTHWEST: + idKwSasiedni.x = x - 1; + idKwSasiedni.y = y + 1; + break; + default: + } + } + + public GridCoord[] dajSasiadow() { + GridCoord[] sasiedzi = new GridCoord[8]; + sasiedzi[0] = new GridCoord(x, y + 1); + sasiedzi[1] = new GridCoord(x + 1, y + 1); + sasiedzi[2] = new GridCoord(x + 1, y); + sasiedzi[3] = new GridCoord(x + 1, y - 1); + sasiedzi[4] = new GridCoord(x, y - 1); + sasiedzi[5] = new GridCoord(x - 1, y - 1); + sasiedzi[6] = new GridCoord(x - 1, y); + sasiedzi[7] = new GridCoord(x - 1, y + 1); + return sasiedzi; + } + + public static GridCoord[] dajSasiadow(int x, int y) { + GridCoord[] sasiedzi = new GridCoord[8]; + sasiedzi[0] = new GridCoord(x, y + 1); + sasiedzi[1] = new GridCoord(x + 1, y + 1); + sasiedzi[2] = new GridCoord(x + 1, y); + sasiedzi[3] = new GridCoord(x + 1, y - 1); + sasiedzi[4] = new GridCoord(x, y - 1); + sasiedzi[5] = new GridCoord(x - 1, y - 1); + sasiedzi[6] = new GridCoord(x - 1, y); + sasiedzi[7] = new GridCoord(x - 1, y + 1); + return sasiedzi; + } + + public static GridCoord[] dajSasiadow(GridCoord centralny) { + GridCoord[] sasiedzi = new GridCoord[8]; + sasiedzi[0] = new GridCoord(centralny.x, centralny.y + 1); + sasiedzi[1] = new GridCoord(centralny.x + 1, centralny.y + 1); + sasiedzi[2] = new GridCoord(centralny.x + 1, centralny.y); + sasiedzi[3] = new GridCoord(centralny.x + 1, centralny.y - 1); + sasiedzi[4] = new GridCoord(centralny.x, centralny.y - 1); + sasiedzi[5] = new GridCoord(centralny.x - 1, centralny.y - 1); + sasiedzi[6] = new GridCoord(centralny.x - 1, centralny.y); + sasiedzi[7] = new GridCoord(centralny.x - 1, centralny.y + 1); + return sasiedzi; + } + + public GridCoord[] dajOtoczenie() { + GridCoord[] sasiedzi = new GridCoord[9]; + sasiedzi[0] = new GridCoord(x, y); + sasiedzi[1] = new GridCoord(x, y + 1); + sasiedzi[2] = new GridCoord(x + 1, y + 1); + sasiedzi[3] = new GridCoord(x + 1, y); + sasiedzi[4] = new GridCoord(x + 1, y - 1); + sasiedzi[5] = new GridCoord(x, y - 1); + sasiedzi[6] = new GridCoord(x - 1, y - 1); + sasiedzi[7] = new GridCoord(x - 1, y); + sasiedzi[8] = new GridCoord(x - 1, y + 1); + return sasiedzi; + } + + public static GridCoord[] dajOtoczenie(int x, int y) { + GridCoord[] sasiedzi = new GridCoord[9]; + sasiedzi[0] = new GridCoord(x, y); + sasiedzi[1] = new GridCoord(x, y + 1); + sasiedzi[2] = new GridCoord(x + 1, y + 1); + sasiedzi[3] = new GridCoord(x + 1, y); + sasiedzi[4] = new GridCoord(x + 1, y - 1); + sasiedzi[5] = new GridCoord(x, y - 1); + sasiedzi[6] = new GridCoord(x - 1, y - 1); + sasiedzi[7] = new GridCoord(x - 1, y); + sasiedzi[8] = new GridCoord(x - 1, y + 1); + return sasiedzi; + } + + public static GridCoord[] dajOtoczenie(GridCoord centralny) { + GridCoord[] sasiedzi = new GridCoord[8]; + sasiedzi[0] = new GridCoord(centralny.x, centralny.y); + sasiedzi[1] = new GridCoord(centralny.x, centralny.y + 1); + sasiedzi[2] = new GridCoord(centralny.x + 1, centralny.y + 1); + sasiedzi[3] = new GridCoord(centralny.x + 1, centralny.y); + sasiedzi[4] = new GridCoord(centralny.x + 1, centralny.y - 1); + sasiedzi[5] = new GridCoord(centralny.x, centralny.y - 1); + sasiedzi[6] = new GridCoord(centralny.x - 1, centralny.y - 1); + sasiedzi[7] = new GridCoord(centralny.x - 1, centralny.y); + sasiedzi[8] = new GridCoord(centralny.x - 1, centralny.y + 1); + return sasiedzi; + } + + public boolean rowne(int x, int y) { + return this.x == x && this.y == y; + } + + public String toString() { + StringBuilder sb = new StringBuilder("(x= "); + sb.append(x); + sb.append(", y= "); + sb.append(y); + sb.append(')'); +// String s = "(x= " + Integer.toString(x) + ", y= " + Integer.toString(y) + ")"; + return sb.toString(); + } + + /** + * Zamienia współrzęną GridCoord.x na długość geograficzną środka małego kwadratu + * + * @param idX współrzęna x GridCoord + * @return długość geograficzna + */ + public static float zamienIdKwadratuXNaDlugoscGeo(int idX) { + long xms = zamienIdKwadratuXNaWspXms(idX); + double lon = (double) xms / (double) MapConsts.DEG_MS - 180; + return (float) lon; + } + + /** + * Zamienia współrzęną GridCoord.y na szerokość geograficzną środka małego kwadratu + * + * @param idY współrzęna y GridCoord + * @return szerokość geograficzna + */ + public static float zamienIdKwadratuYNaSzerokoscGeo(int idY) { + long yms = zamienIdKwadratuYNaWspYms(idY); + double lat = (double) yms / (double) MapConsts.DEG_MS - 90; + return (float) lat; + } + + /** + * Zamienia współrzęną GridCoord.x na długość geograficzną lewego dolnego rogu małego kwadratu + * + * @param idX współrzęna x GridCoord + * @return długość geograficzna + */ + public static float zamienIdKwadratuXNaDlugoscGeoLD(int idX) { + long xms = zamienIdKwadratuXNaWspXmsLD(idX); + double lon = (double) xms / (double) MapConsts.DEG_MS - 180; + return (float) lon; + } + + /** + * Zamienia współrzęną GridCoord.y na szerokość geograficzną środka małego kwadratu + * + * @param idY współrzęna y GridCoord + * @return szerokość geograficzna + */ + public static float zamienIdKwadratuYNaSzerokoscGeoLD(int idY) { + long yms = zamienIdKwadratuYNaWspYmsLD(idY); + double lat = (double) yms / (double) MapConsts.DEG_MS - 90; + return (float) lat; + } + + public static int zamienWspUtmNaIdkwadratuX(String wspolrzedneUtm) { + // ////////////////////////////////// + // wspolrzedne - znaki w formacie 522644N0154232E + // wyznaczenie numeru kwadraty Au2 dla wspolrzednej X + // np.: '153200' to 304 + + int xms = Teren.zamienWspUtmNaWspXms(wspolrzedneUtm); + int idKwX = GridCoord.zamienWspXmsNaIdKwadratuX(xms); + return idKwX; + } + + public static int zamienWspUtmNaIdkwadratuY(String wspolrzedneUtm) { + // ////////////////////////////////// + // wspolrzedne - znaki w formacie 522644N0154232E + // wyznaczenie numeru kwadraty Au2 dla wspolrzednej Y + // np.: '153200' to 304 + int yms = Teren.zamienWspUtmNaWspYms(wspolrzedneUtm); + int idKwY = GridCoord.zamienWspYmsNaIdKwadratuY(yms); + return idKwY; + } + + public GridCoord(String wspolrzedne) { + this.x = zamienWspUtmNaIdkwadratuX(wspolrzedne); + this.y = zamienWspUtmNaIdkwadratuY(wspolrzedne); + } + + public static GridCoord zamienWspUtmNaIdKwadratu(String wspolrzedne) { + // Utworzenie obiektu GridCoord ze wspolrzednych UTM + GridCoord kwadrat = new GridCoord(wspolrzedne); + return kwadrat; + } + + public static String zamienIdKwadratuNaWspUtm(GridCoord kwadrat) { + long xms = zamienIdKwadratuXNaWspXms(kwadrat.x); + long yms = zamienIdKwadratuYNaWspYms(kwadrat.y); + return Teren.zamienWspXmsYmsNaWspUtm(xms, yms); + } + + /** + * Funkcja zamienia wsp. X GridCoord na wsp. geo xms w milisekundach. + * Zwracana wspolrzedna lezy w srodku kwadratu. + */ + public static long zamienIdKwadratuXNaWspXms(int idKwX) { + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + // przesuniecie wspolrzednych do srodka kwadratu + long xms = MapConsts.X_REF_MS + (long) ((idKwX + 0.5) * MapConsts.SS_DX_MS); + xms %= MapConsts.ANGLE_360_MS; + return xms; + } + + /** + * Funkcja zamienia wsp. Y GridCoord na wsp. geo yms w milisekundach. + * Zwracana wspolrzedna lezy w srodku kwadratu. + */ + public static long zamienIdKwadratuYNaWspYms(int idKwY) { + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + // przesuniecie wspolrzednych do srodka kwadratu + long yms = MapConsts.Y_REF_MS + (long) ((idKwY + 0.5) * MapConsts.SS_DY_MS); + return yms; + } + + /** + * Funkcja zamienia wsp. X GridCoord na wsp. geo xms w milisekundach lewego dolnego rogu malego kwadratu. + * Zwracana wspolrzedna lezy w srodku kwadratu. + */ + public static long zamienIdKwadratuXNaWspXmsLD(int idKwX) { + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + // przesuniecie wspolrzednych do srodka kwadratu + long xms = MapConsts.X_REF_MS + (long) (idKwX * MapConsts.SS_DX_MS); + xms %= MapConsts.ANGLE_360_MS; + return xms; + } + + /** + * Funkcja zamienia wsp. Y GridCoord na wsp. geo yms w milisekundach lewego dolnego rogu malego kwadratu. + * Zwracana wspolrzedna lezy w srodku kwadratu. + */ + public static long zamienIdKwadratuYNaWspYmsLD(int idKwY) { + // indeksowanie kwadratow pola walki zaczyna sie od (0, 0) + // przesuniecie wspolrzednych do srodka kwadratu + long yms = MapConsts.Y_REF_MS + (long) (idKwY * MapConsts.SS_DY_MS); + return yms; + } + + private static final double ODWROT_SS_DX_MS = 1.0 / MapConsts.SS_DX_MS; + private static final double ODWROT_SS_DY_MS = 1.0 / MapConsts.SS_DY_MS; + + /** + * Funkcja sprawdza, czy kwadraty sa takie same. + * + * @param id1 + * @param id2 + * @return + */ + public static boolean czyIdentyczne(GridCoord id1, GridCoord id2) { + if (null == id1 || null == id2) { + return false; + } + if (id1.x != id2.x || id1.y != id2.y) { + return false; + } else + return true; + } + + /** + * 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(GridCoord p1, GridCoord 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.DL_MK; + return odl; + } + + /** + * Funkcja zwraca odległość między środkami małych kwadratów [m]. + * + * @param x1 Wsp X id malego kwadratu pocz. + * @param y1 Wsp Y id malego kwadratu pocz. + * @param x2 Wsp X id malego kwadratu konc. + * @param y2 Wsp Y id malego kwadratu konc. + * @return Odległość miedzy srodkami malych kwadratow [m]. + */ + public static float odleglosc(int x1, int y1, int x2, int y2) { + // odleglosc miedzy srodkami malych kwadratow + int xx = x1 - x2; + xx *= xx; + int yy = y1 - y2; + yy *= yy; + float odl = (float) Math.sqrt(xx + yy); + odl *= MapConsts.DL_MK; + 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.DL_MK; + return odl; + } + + private static final float DL_MK2 = MapConsts.DL_MK * 0.0009765625f; + /** + * Funkcja zwraca aproksymowaną odległość między środkami małych kwadratów [m]. + * + * @param x1 Wsp X id malego kwadratu pocz. + * @param y1 Wsp Y id malego kwadratu pocz. + * @param x2 Wsp X id malego kwadratu konc. + * @param y2 Wsp Y id malego kwadratu konc. + * @return Odległość miedzy srodkami malych kwadratow [m]. + */ + public static float odlegloscApprox(int x1, int y1, int x2, int y2) { + int dx = x2 - x1; + int dy = y2 - y1; + int min, max, approx; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + if (dx < dy) { + min = dx; + max = dy; + } else { + min = dy; + max = dx; + } + approx = (max * 1007) + (min * 441); + if (max < (min << 4)) { + approx -= (max * 40); + } + float odl = approx * DL_MK2; + return odl; + } + + public static int iloczynSkalarny(GridCoord p1, GridCoord p2) { + int wynik; + wynik = p1.x * p2.x + p1.y * p2.y; + return wynik; + } + + // funkcja sprawdza, czy dwa obszary skierowane maja czesc wspolna + public static boolean majaCzescWspolna(GridCoord[] rejon1, GridCoord[] rejon2) { + if ((rejon1 == null) || (rejon2 == null)) { + return false; + } + for (int i = 0; i < rejon2.length; i++) { + if (GeomUtils.insidePolygon(rejon1, rejon2[i].x, rejon2[i].y)) { + return true; + } + } + for (int i = 0; i < rejon1.length; i++) { + if (GeomUtils.insidePolygon(rejon2, rejon1[i].x, rejon1[i].y)) { + return true; + } + } + return false; + } + + // wyznacza stosunek czesci wspolnej obszarow do obszaru bazowego + public static float stosunekCzesciWspolnej(GridCoord[] rejon_bazowy, GridCoord[] rejon2) { + if ((rejon_bazowy == null) || (rejon2 == null)) { + return 0.0f; + } + boolean majaCzescWspolna = majaCzescWspolna(rejon_bazowy, rejon2); + if (!majaCzescWspolna) { + return 0.0f; + } + GridCoord[] kwadraty1 = GeomUtils.kwadratyObszaru(rejon_bazowy); + GridCoord[] kwadraty2 = GeomUtils.kwadratyObszaru(rejon2); + int maxWspolnych = 0; + if (kwadraty1.length <= kwadraty2.length) { + maxWspolnych = kwadraty1.length; + } else { + maxWspolnych = kwadraty2.length; + } + if ((kwadraty1 == null) || (kwadraty2 == null)) { + return 0.0f; + } + int wspolnych = 0; + for (int i = 0; i < kwadraty1.length; i++) { + for (int j = 0; j < kwadraty2.length; j++) { + if (czyIdentyczne(kwadraty1[i], kwadraty2[j])) { + wspolnych++; + } + } + } + return ((float) wspolnych) / ((float) (maxWspolnych)); + } + + // wyznacza wspolrzedne lewego dolnego wierzcholka oraz + // prawego gornego wierzcholka prostokata opisanego na obszarze: rejon + public static void minMaxIdKwadratu(GridCoord[] rejon, GridCoord min, GridCoord max) { + if (rejon == null) { + return; + } + if (min == null) { + min = new GridCoord(rejon[0].x, rejon[0].y); + } + if (max == null) { + max = new GridCoord(rejon[0].x, rejon[0].y); + } + for (int i = 1; i < rejon.length; i++) { + if (min.x > rejon[i].x) { + min.x = rejon[i].x; + } + if (min.y > rejon[i].y) { + min.y = rejon[i].y; + } + if (max.x < rejon[i].x) { + max.x = rejon[i].x; + } + if (max.y < rejon[i].y) { + max.y = rejon[i].y; + } + } + } + + public void update(double lon, double lat) { + double xms_f = (lon + 180) * MapConsts.DEG_MS; + long xms = (long) xms_f; + x = zamienWspXmsNaIdKwadratuX(xms); + + double yms_f = (lat + 90) * MapConsts.DEG_MS; + long yms = (long) yms_f; + y = zamienWspYmsNaIdKwadratuY(yms); + } + + public GridCoord() { + x = -1; + y = -1; + } + + public GridCoord(int x, int y) { +/* try { + if (x < 0 || y < 0) + throw new IdKwadratuException(); + } + catch (IdKwadratuException e) { + System.out.print(e.opis); + } +*/ + this.x = x; + this.y = y; + } + + public GridCoord(GridCoord oryginal) { + this.x = oryginal.x; + this.y = oryginal.y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public void set(GridCoord oryginal) { + x = oryginal.x; + y = oryginal.y; + } + + public void set(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = prime + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof GridCoord)) { + return false; + } + GridCoord other = (GridCoord) obj; + if (x != other.x) { + return false; + } + if (y != other.y) { + return false; + } + return true; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/Kwadrat.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/Kwadrat.java new file mode 100644 index 0000000..55bbfb9 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/Kwadrat.java @@ -0,0 +1,358 @@ +package pl.wat.ms4ds.terenfunkcje; + + +import pl.wat.ms4ds.common.EGeoDirection; + +public class Kwadrat { + + public static final Kwadrat EMPTY_SQUARE = new Kwadrat(null, 0.0f, 0.0f, 0.0f, 0.0f, 200, 0, + new boolean[8], new boolean[8], new boolean[8]); + + float stopienZabudowy; + float stopienZalesienia; + float stopienZawodnienia; + float stopienZabagnienia; + boolean jestDroga[]; + boolean jestRow[]; + boolean jestPrzeszkodaWodna[]; + int roznicaWzniesien; + int wysokoscSrednia; + + /** + * The height above the level of the sea. + */ + short elevation; + /** + * 0 - BARE_GROUND + * 1 - GRASS + * 2 - SWAMP + * 3 - WATER + * 4 - SCRUB, BUSHES + * 5 - BUILDINGS + * 6 - FOREST + */ + short terrainType; + /** + * Rodzaj drogi na danym kierunku. + * 0 - no road, 1 - small roads, 2 - minor roads, 3 - major roads + */ + byte[] roads; + /** + * Rodzaj przeszkody wodnej na danym kierunku. + * 0 - no watercourse, 1 - drain, ditch, 2 - canal, stream, 3 - river + */ + byte[] waterWays; + + + + transient RightBigSquare bs; + + public float getStopienZabudowy() { + return stopienZabudowy; + } + + public float getStopienZalesienia() { + return stopienZalesienia; + } + + public float getStopienZawodnienia() { + return stopienZawodnienia; + } + + public float getStopienZabagnienia() { + return stopienZabagnienia; + } + + public int getRoznicaWzniesien() { + return roznicaWzniesien; + } + + public int getWysokoscSrednia() { + return wysokoscSrednia; + } + + public boolean[] getJestDroga() { + return jestDroga; + } + + public boolean jestOdcinekDrogi() { + for (int i = 0; i < 8; i++) { + if (jestDroga[i]) + return true; + } + return false; + } + + public boolean getJestDroga(EGeoDirection naKierunku) { + if (naKierunku == EGeoDirection.UNDEFINED) { + return false; + } + return jestDroga[naKierunku.id]; + } + + public boolean[] getJestRow() { + return jestRow; + } + + public boolean[] getJestPrzeszkodaWodna() { + return jestPrzeszkodaWodna; + } + + public void setStopienZabudowy(float stopienZabudowy) { + this.stopienZabudowy = stopienZabudowy; + normalizujDanePokrycia(); +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setStopienZalesienia(float stopienZalesienia) { + this.stopienZalesienia = stopienZalesienia; + normalizujDanePokrycia(); +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setStopienZawodnienia(float stopienZawodnienia) { + this.stopienZawodnienia = stopienZawodnienia; + normalizujDanePokrycia(); +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setStopienZabagnienia(float stopienZabagnienia) { + this.stopienZabagnienia = stopienZabagnienia; + normalizujDanePokrycia(); +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void normalizujDanePokrycia() { + float suma = stopienZalesienia + stopienZawodnienia + stopienZabudowy + stopienZabagnienia; + if (suma > 1.0f) { + stopienZalesienia /= suma; + stopienZawodnienia /= suma; + stopienZabudowy /= suma; + stopienZabagnienia = 1.0f - stopienZalesienia - stopienZawodnienia - stopienZabudowy; + } + } + + public void setRoznicaWzniesien(int roznicaWzniesien) { + this.roznicaWzniesien = roznicaWzniesien; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setWysokoscSrednia(int wysokoscSrednia) { + this.wysokoscSrednia = wysokoscSrednia; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestDroga(boolean[] czyJestDroga) { + this.jestDroga = czyJestDroga; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestDroga(EGeoDirection naKierunku, boolean czyJest) { + if (naKierunku == EGeoDirection.UNDEFINED) { + return; + } + this.jestDroga[naKierunku.id] = czyJest; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestPrzeszkodaWodna(EGeoDirection naKierunku, boolean czyJest) { + if (naKierunku == EGeoDirection.UNDEFINED) { + return; + } + this.jestPrzeszkodaWodna[naKierunku.id] = czyJest; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestRow(EGeoDirection naKierunku, boolean czyJest) { + if (naKierunku == EGeoDirection.UNDEFINED) { + return; + } + this.jestRow[naKierunku.id] = czyJest; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestRow(boolean[] czyJestRow) { + this.jestRow = czyJestRow; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public void setJestPrzeszkodaWodna(boolean[] czyJestPrzeszkodaWodna) { + this.jestPrzeszkodaWodna = czyJestPrzeszkodaWodna; +// try { +// if (null != bs) { +// bs.updateFile(false); +// } +// } catch (IOException e) { +// } + } + + public float getStopienPofaldowania() { + // TODO: dodac do normatywow w klasie Teren (parametr kalibracyjny) + float stopienPofaldowania = 0.0f; + if (this.roznicaWzniesien >= 50) + stopienPofaldowania = 1.0f; + else if (this.roznicaWzniesien >= 10) + stopienPofaldowania = 0.6f; + else if (this.roznicaWzniesien >= 5) + stopienPofaldowania = 0.4f; + else if (this.roznicaWzniesien >= 3) + stopienPofaldowania = 0.2f; + return stopienPofaldowania; + } + + public Kwadrat() { + jestDroga = new boolean[8]; + jestPrzeszkodaWodna = new boolean[8]; + jestRow = new boolean[8]; + } + + public Kwadrat(RightBigSquare _bs, float _stopienZabudowy, float _stopienZalesienia, float _stopienZabagnienia, float _stopienZawodnienia, + int _wysokoscSrednia, int _roznicaWzniesien, boolean _czyJestDroga[], boolean _czyJestRow[], boolean _czyJestPrzeszkodaWodna[]){ + bs = _bs; + stopienZabudowy = _stopienZabudowy; + stopienZalesienia = _stopienZalesienia; + stopienZabagnienia = _stopienZabagnienia; + stopienZawodnienia = _stopienZawodnienia; + wysokoscSrednia = _wysokoscSrednia; + roznicaWzniesien = _roznicaWzniesien; + jestDroga = new boolean[8]; + jestRow = new boolean[8]; + jestPrzeszkodaWodna = new boolean[8]; + for (int i = 0; i < _czyJestDroga.length; i++) { + jestDroga[i]=_czyJestDroga[i]; + } + for (int i = 0; i < jestRow.length; i++) { + jestRow[i]=_czyJestRow[i]; + } + for (int i = 0; i < _czyJestPrzeszkodaWodna.length; i++) { + jestPrzeszkodaWodna[i]=_czyJestPrzeszkodaWodna[i]; + } + } + + public String toString() { + float f = this.stopienZabudowy * 255.0f; + int hex = (int)f; + String s = String.format("%02X", hex); + String linia = s; + linia += " "; + + f = this.stopienZalesienia * 255.0f; + hex = (int)f; + s = String.format("%02X", hex); + linia += s; + linia += " "; + + f = this.stopienZabagnienia * 255.0f; + hex = (int)f; + s = String.format("%02X", hex); + linia += s; + linia += " "; + + f = this.stopienZawodnienia * 255.0f; + hex = (int)f; + s = String.format("%02X", hex); + linia += s; + linia += " "; + + s = String.format("%4d", this.wysokoscSrednia); + linia += s; + linia += " "; + + s = String.format("%4d", this.roznicaWzniesien); + linia += s; + linia += " "; + + int bity = 0; + int bit_1 = 1; + for (int i = 0; i < this.jestDroga.length; i++) { + if (this.jestDroga[i]) // jest odcinek drogi na tym kierunku + bity += bit_1; + + bit_1 = bit_1 << 1; + } + s = String.format("%02X", bity); + linia += s; + linia += " "; + + bity = 0; + bit_1 = 1; + for (int i = 0; i < this.jestPrzeszkodaWodna.length; i++) { + if (this.jestPrzeszkodaWodna[i]) // jest przeszkod na tym kierunku + bity += bit_1; + + bit_1 = bit_1 << 1; + } + s = String.format("%02X", bity); + linia += s; + linia += " "; + + bity = 0; + bit_1 = 1; + for (int i = 0; i < this.jestRow.length; i++) { + if (this.jestRow[i]) // jest row na tym kierunku + bity += bit_1; + + bit_1 = bit_1 << 1; + } + s = String.format("%02X", bity); + linia += s; + return linia; + } + + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/LukDrogowy.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/LukDrogowy.java new file mode 100644 index 0000000..bf3d98d --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/LukDrogowy.java @@ -0,0 +1,113 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.util.StringTokenizer; + +public class LukDrogowy { + + private WezelDrogowy[] wezly = new WezelDrogowy[2]; + int id = -1; + int dlugosc = 0; + int szerokosc = 0; + int rodzajNawierzchni = 0; + boolean jestMostem = false; + + LukDrogowy() {} + + LukDrogowy(String opis) { + if (null == opis || opis.length() == 0){ + SiecDrogowa.logger.debug("Pusty ciag opis w konstruktorze Luku Drogowego."); + return; + } + StringTokenizer st = new StringTokenizer(opis, " \t"); + String[] tokenTable = new String[st.countTokens()]; + for (int i = 0; st.hasMoreTokens(); i++) { + tokenTable[i] = st.nextToken(); + } + if (tokenTable.length == 6) { + try { + int id = Integer.parseInt(tokenTable[0]); + WezelDrogowy wezel = SiecDrogowa.instancja.wezly.get(id); + if (null == wezel) + SiecDrogowa.logger.warn("Brak wierzcholka (1) poczatkowego luku. [" + opis + "]."); + this.getWezly()[0] = wezel; + wezel.luki.add(this); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [Wezel 1]."); + } + try { + int id = Integer.parseInt(tokenTable[1]); + WezelDrogowy wezel = SiecDrogowa.instancja.wezly.get(id); + if (null == wezel) + SiecDrogowa.logger.warn("Brak wierzcholka (2) koncowego luku. [" + opis + "]."); + this.getWezly()[1] = wezel; + wezel.luki.add(this); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [Wezel 2]."); + } + if (this.wezly[0].id == this.wezly[1].id) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [wezly sa identyczne]."); + } + if (GridCoord.czyIdentyczne(this.wezly[0].idKw, this.wezly[1].idKw)) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [wezly sa identyczne]."); + } + try { + this.dlugosc = Integer.parseInt(tokenTable[2]); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [Dlugosc]."); + } + try { + this.szerokosc = Integer.parseInt(tokenTable[3]); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [Szerokosc]."); + } + try { + this.rodzajNawierzchni = Integer.parseInt(tokenTable[4]); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [RodzajNawierzchni]."); + } + try { + int b = Integer.parseInt(tokenTable[5]); + this.jestMostem = (b != 0); + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z lukami [CzyJestMost]."); + } + } + } + + public int getDlugosc() { + return dlugosc; + } + + public void setDlugosc(int dlugosc) { + this.dlugosc = dlugosc; + } + + public int getSzerokosc() { + return szerokosc; + } + + public void setSzerokosc(int szerokosc) { + this.szerokosc = szerokosc; + } + + public int getRodzajNawierzchni() { + return rodzajNawierzchni; + } + + public void setRodzajNawierzchni(int rodzajNawierzchni) { + this.rodzajNawierzchni = rodzajNawierzchni; + } + + public boolean isJestMostem() { + return jestMostem; + } + + public void setJestMostem(boolean jestMostem) { + this.jestMostem = jestMostem; + } + + public WezelDrogowy[] getWezly() { + return wezly; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/MapConsts.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/MapConsts.java new file mode 100644 index 0000000..382835c --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/MapConsts.java @@ -0,0 +1,242 @@ +package pl.wat.ms4ds.terenfunkcje; + +import org.apache.log4j.Logger; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +public final class MapConsts { + + private static final Logger LOGGER = Logger.getLogger(MapConsts.class); + + /** + * Umowny uklad odniesienia dla lokalizacji geograficznej:

+ * Długość geograficzna (wsp. X) przyjmuje wartości: [0, 360) odpowiadające [-180, 180]

+ * Szerokość geograficzna (wsp. Y) przyjmuje wartości: [0, 180] odpowadające [-90, 90] + */ + public static final int X_REF; + /** + * Umowny uklad odniesienia dla lokalizacji geograficznej:

+ * Długość geograficzna (wsp. X) przyjmuje wartości: [0, 360) odpowiadające [-180, 180]

+ * Szerokość geograficzna (wsp. Y) przyjmuje wartości: [0, 180] odpowadające [-90, 90] + */ + public static final int Y_REF; + public static final int DX_REF; + public static final int DY_REF; + + public static final String KWADRATY_DIR; + public static final String DROGI_DIR; + + /** + * Nazwa pliku z konfiguracja mechanizmu odpowiedzialnego za transfer. Plik + * musi znajdowac sie w katalogu glownym aplikacji, ewentualnie musi tu byc + * podana sciezka bezwzgledna do niego. + */ + private static final String PLIK_Z_USTAWIENIAMI = "teren.properties"; + + /** + * Dlugosc boku duzego kwadratu na osi OX w liczbie malych kwadratow. + */ + public static final int SS_PER_BS_X; + /** + * Dlugosc boku duzego kwadratu na osi OY w liczbie malych kwadratow. + */ + public static final int SS_PER_BS_Y; + /** + * Dlugosc boku malego kwadratu w metrach. + */ + public static final int DL_MK; + /** + * Powierzchnia malego kwadratu w metrach. + */ + public static final int POW_MK; + /** + * Szerokość małego kwadratu w stopniach. + */ + public static final double DELTA_X; + /** + * Wysokość małego kwadratu w stopniach. + */ + public static final double DELTA_Y; + /** + * Liczba duzych kwadratow na stopien geograficzny po osi OX (dlugosc geograficzna). + */ + public static final int BS_PER_DEG_X = 4; + /** + * Liczba duzych kwadratow na stopien geograficzny po osi OY (szerokosc geograficzna). + */ + public static final int BS_PER_DEG_Y = 6; + /** + * Szerokość duzych kwadratow w stopniach geograficznych po osi OX (dlugosc geograficzna). + */ + public static final double BS_DX; + /** + * Wysokość duzych kwadratow w stopniach geograficznych po osi OY (szerokosc geograficzna). + */ + public static final double BS_DY; + + static Properties ustawienia; + + /** + * Odczytanie ustawien z pliku konfiguracyjnego. + */ + static { + String propertiesFileName = System.getProperty("user.dir") + "\\" + PLIK_Z_USTAWIENIAMI; + + ustawienia = new Properties(); + try { + LOGGER.debug("Odczyt ustawien z pliku " + propertiesFileName + "."); + ustawienia.load(new FileInputStream(propertiesFileName)); + LOGGER.debug("Ustawienia wczytane."); + } catch (FileNotFoundException e) { + LOGGER.error(e.getLocalizedMessage()); + } catch (IOException e) { + LOGGER.error(e.getLocalizedMessage()); + } +// logger.debug("Ustawienia wczytane."); + // przesuniecie o 180 stop. + // poludnik zerowy ma wartosc 180, zatem wspolrzedne zachodnie (ujemne) zawierają sie w <0, 180) + // wspolrzedne wschodnie (nieujemne) zawieraja sie w przedziale <180, 360) + X_REF = Integer.parseInt(ustawienia.getProperty("x_ref")) + 180; + // przesuniecie o 90 stop. + // rownik ma wartosc 90, zatem wspolrzedne poludniowe (ujemne) zawierają sie w <0, 90) + // wspolrzedne polnocne (nieujemne) zawieraja sie w przedziale <90, 180> + Y_REF = Integer.parseInt(ustawienia.getProperty("y_ref")) + 90; + DX_REF = Integer.parseInt(ustawienia.getProperty("dx_ref")); + DY_REF = Integer.parseInt(ustawienia.getProperty("dy_ref")); + String val = ustawienia.getProperty("dl_mk"); + switch (val) { + case "20": + DL_MK = 20; + break; + case "25": + DL_MK = 25; + break; + case "50": + DL_MK = 50; + break; + case "100": + DL_MK = 100; + break; + default: + DL_MK = 200; + break; + } + POW_MK = DL_MK * DL_MK; + DROGI_DIR = ustawienia.getProperty("drogi_dir"); + if (DL_MK == 20) { + SS_PER_BS_X = 83 * 10; + SS_PER_BS_Y = 93 * 10; + DELTA_X = 1.0 / (double) (BS_PER_DEG_X * SS_PER_BS_X); + DELTA_Y = 1.0 / (double) (BS_PER_DEG_Y * SS_PER_BS_Y); + KWADRATY_DIR = ustawienia.getProperty("kwadraty_dir") + "20m/"; + } else if (DL_MK == 25) { + SS_PER_BS_X = 83 * 8; + SS_PER_BS_Y = 93 * 8; + DELTA_X = 1.0 / (double) (BS_PER_DEG_X * SS_PER_BS_X); + DELTA_Y = 1.0 / (double) (BS_PER_DEG_Y * SS_PER_BS_Y); + KWADRATY_DIR = ustawienia.getProperty("kwadraty_dir") + "25m/"; + } else if (DL_MK == 50) { + SS_PER_BS_X = 83 * 4; + SS_PER_BS_Y = 93 * 4; + DELTA_X = 1.0 / (double) (BS_PER_DEG_X * SS_PER_BS_X); + DELTA_Y = 1.0 / (double) (BS_PER_DEG_Y * SS_PER_BS_Y); + KWADRATY_DIR = ustawienia.getProperty("kwadraty_dir") + "50m/"; + } else if (DL_MK == 100) { + SS_PER_BS_X = 83 * 2; + SS_PER_BS_Y = 93 * 2; + DELTA_X = 1.0 / (double) (BS_PER_DEG_X * SS_PER_BS_X); + DELTA_Y = 1.0 / (double) (BS_PER_DEG_Y * SS_PER_BS_Y); + KWADRATY_DIR = ustawienia.getProperty("kwadraty_dir") + "100m/"; + } else { + // domyslnie dlugosc kwadratu 200m + SS_PER_BS_X = 83; + SS_PER_BS_Y = 93; + DELTA_X = 1.0 / (double) (BS_PER_DEG_X * SS_PER_BS_X); + DELTA_Y = 1.0 / (double) (BS_PER_DEG_Y * SS_PER_BS_Y); + KWADRATY_DIR = ustawienia.getProperty("kwadraty_dir") + "200m/"; + } + BS_DX = 1.0 / (double) BS_PER_DEG_X; + BS_DY = 1.0 / (double) BS_PER_DEG_Y; + } + + /** + * Liczba milisekund na stopien. + */ + public static final int DEG_MS = 3600000; + /** + * Liczba milisekund na 360 stopni. + */ + public static final long ANGLE_360_MS = 3600000 * 360; + /** + * Wielkosc cache'u pola walki (liczba duzych kwadratow trzymanych w RAM). + */ + public static final int MAX_BIG_SQUARES_IN_MEMORY = 500; + /** + * Wspolrzedna referencyjna X (dlugosc geograficzna) lewego dolnego rogu mapy w stopniach geograficznych. + * @return + */ + public static int getX_REF() { + return X_REF; + } + + /** + * Wspolrzedna referencyjna Y (szerokosc geograficzna) lewego dolnego rogu mapy w stopniach geograficznych. + * @return + */ + public static int getY_REF() { + return Y_REF; + } + + /** + * Szerokosc referencyjna prostokata pola walki w stopniach na osi OX (dlugosc geograficzna). + * @return + */ + public static int getDX_REF() { + return DX_REF; + } + + /** + * Wysokosc referencyjna prostokata pola walki w stopniach na osi OY (szerokosc geograficzna). + * @return + */ + public static int getDY_REF() { + return DY_REF; + } + + public static String getKwadratyDir() { + return KWADRATY_DIR; + } + + public static String getDrogiDir() { + return DROGI_DIR; + } + /** + * Dlugosci bokow malego kwadratu w milisekundach geograficznych po osi OX (dlugosc geograficzna). + */ + public static final double SS_DX_MS = DELTA_X * DEG_MS; + /** + * Dlugosci bokow malego kwadratu w milisekundach geograficznych po osi OY (szerokosc geograficzna). + */ + public static final double SS_DY_MS = DELTA_Y * DEG_MS; + // wspolrzedne dolnego lewego rogu mapy w ms + // wspolrzedne geograficzne w milisekundach zawieraja sie w zakresie: + // 0 <= x < 360 dlugosc geograficzna + // 0 <= y <= 180 szerokosc geograficzna + public static final int X_REF_MS = X_REF * DEG_MS; + public static final int Y_REF_MS = Y_REF * DEG_MS; + public static final int DX_REF_MS = DEG_MS * DX_REF; // szerokosc pola walki w stopniach + public static final int DY_REF_MS = DEG_MS * DY_REF; // wysokosc polwa walki w stopniach + + public static final int BS_DX_MS = (int) (BS_DX * DEG_MS); + public static final int BS_DY_MS = (int) (BS_DY * DEG_MS); + + /** + * Liczba malych kwadratow przypadajaca na bok arkusza mapy drogowej. + */ + public static final int SS_PER_SHEET = 20; + + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/RightBigSquare.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/RightBigSquare.java new file mode 100644 index 0000000..7f1d224 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/RightBigSquare.java @@ -0,0 +1,640 @@ +package pl.wat.ms4ds.terenfunkcje; + +import org.apache.log4j.Logger; + +import java.io.*; + +public class RightBigSquare extends BigSquare implements Serializable { + + private static final Logger logger = Logger.getLogger(RightBigSquare.class); + + private Kwadrat kwadraty[][]; + + Kwadrat getKwadrat(int ssX, int ssY) { + return kwadraty[ssX][ssY]; + } + + /** + * Funkcja zapisująca do pliku duży kwadrat.

+ * Zapis następuje w trybie wskazywanym przez atrybut: RightBigSquare.binary. + * + * @param wymuszony flag informujacy o trybie zapisu pliku + * @param fName nazwa pliku + * @throws IOException generowany wyjątek + */ + void updateFile(boolean wymuszony, String fName) throws IOException { + if (wymuszony) { + if (this.liczbaZmian == 0) { + // nie bylo modyfikacji od ostatniego zapisu + return; + } + this.liczbaZmian = -1; + } + this.liczbaZmian++; + this.liczbaZmian %= LICZBA_ZMIAN_DO_ZAPISU; + if (this.liczbaZmian > 0) { + return; + } + if (fName != null) { + fileName = fName; + } + StringBuilder sb = new StringBuilder(100); + sb.append(MapConsts.KWADRATY_DIR); + sb.append(fileName); + sb.append(".bin"); + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(sb.toString())); + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + float f = kwadraty[x][y].stopienZalesienia * 100.0f; + int hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + out.writeByte(hex); + f = kwadraty[x][y].stopienZawodnienia * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + out.writeByte(hex); + f = kwadraty[x][y].stopienZabudowy * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + out.writeByte(hex); + f = kwadraty[x][y].stopienZabagnienia * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + out.writeByte(hex); + out.writeInt(kwadraty[x][y].wysokoscSrednia); + out.writeInt(kwadraty[x][y].roznicaWzniesien); + + hex = 0; + int bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestDroga.length; i++) { + // jest odcinek drogi na tym kierunku + if (kwadraty[x][y].jestDroga[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + out.writeByte(hex); + + hex = 0; + bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestPrzeszkodaWodna.length; i++) { + // jest odcinek przeszkody wodnej na tym kierunku + if (kwadraty[x][y].jestPrzeszkodaWodna[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + out.writeByte(hex); + + hex = 0; + bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestRow.length; i++) { + // jest odcinek rowu na tym kierunku + if (kwadraty[x][y].jestRow[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + out.writeByte(hex); + } + } + out.close(); + logger.debug("Zapisano plik mapy: " + sb.toString()); + } + + static final int NORTH = 0; + static final int NORTH_EAST = 1; + static final int EAST = 2; + static final int SOUTH_EAST = 3; + static final int SOUTH = 4; + static final int SOUTH_WEST = 5; + static final int WEST = 6; + static final int NORTH_WEST = 7; + static final byte NORTH_CODE = 1; + static final byte NORTH_EAST_CODE = 2; + static final byte EAST_CODE = 4; + static final byte SOUTH_EAST_CODE = 8; + static final byte SOUTH_CODE = 16; + static final byte SOUTH_WEST_CODE = 32; + static final byte WEST_CODE = 64; + static final byte NORTH_WEST_CODE = -128; + + + /** + * Funkcja generuje nowy plik z danymi na podstawie danych z pliku referencyjnego (kwadraty o rozm. 200m). + *

Nowy plik moze byc z danymi w innej skali (kwadraty o rozmiarach: 100m lub 50m) i/lub innym formacie (binarny, tekstowy). + * + * @param dir katalog docelowy dla nowego pliku + * @param dlmk rozmiar kwadratow generownych danych + * @throws IOException generowany wyjątek + */ + public void saveNewFileWithNewScale(String dir, int dlmk) throws IOException { + if (MapConsts.DL_MK != 100) { + // operacja tylko dla danych terenowych o kwadratach 200m + return; + } + int m = 1; + String s = ""; + if (dlmk == 100) { + m = 1; + s = "100m/"; + } else if (dlmk == 50) { + m = 2; + s = "50m/"; + } else if (dlmk == 25) { + m = 4; + s = "25m/"; + } else if (dlmk == 20) { + m = 5; + s = "20m/"; + } else { + return; + } + StringBuilder sb = new StringBuilder(100); + sb.append(dir); + sb.append(s); + // Utworzenie katalogów, gdyby nie istniały. + File directory = new File(sb.toString()); + directory.mkdirs(); + sb.append(fileName); + sb.append(".bin"); + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(sb.toString())); + + SmallSquare[][][][] ss_all = new SmallSquare[MapConsts.SS_PER_BS_X][MapConsts.SS_PER_BS_Y][][]; + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + SmallSquare[][] ss = new SmallSquare[m][m]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < m; j++) { + ss[i][j] = new SmallSquare(); + } + } + ss_all[x][y] = ss; + // jest odcinek drogi na tym kierunku + if (kwadraty[x][y].jestDroga[NORTH]) { + ss[2][2].majorRoads |= NORTH_CODE; + ss[2][3].majorRoads |= NORTH_CODE | SOUTH_CODE; + ss[2][4].majorRoads |= NORTH_CODE | SOUTH_CODE; + } + if (kwadraty[x][y].jestDroga[NORTH_EAST]) { + ss[2][2].majorRoads |= NORTH_EAST_CODE; + ss[3][3].majorRoads |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + ss[4][4].majorRoads |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + } + if (kwadraty[x][y].jestDroga[EAST]) { + ss[2][2].majorRoads |= EAST_CODE; + ss[3][2].majorRoads |= EAST_CODE | WEST_CODE; + ss[4][2].majorRoads |= EAST_CODE | WEST_CODE; + } + if (kwadraty[x][y].jestDroga[SOUTH_EAST]) { + ss[2][2].majorRoads |= SOUTH_EAST_CODE; + ss[3][1].majorRoads |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + ss[4][0].majorRoads |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + } + if (kwadraty[x][y].jestDroga[SOUTH]) { + ss[2][2].majorRoads |= SOUTH_CODE; + ss[2][1].majorRoads |= SOUTH_CODE | NORTH_CODE; + ss[2][0].majorRoads |= SOUTH_CODE | NORTH_CODE; + } + if (kwadraty[x][y].jestDroga[SOUTH_WEST]) { + ss[2][2].majorRoads |= SOUTH_WEST_CODE; + ss[1][1].majorRoads |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + ss[0][0].majorRoads |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + } + if (kwadraty[x][y].jestDroga[WEST]) { + ss[2][2].majorRoads |= WEST_CODE; + ss[1][2].majorRoads |= WEST_CODE | EAST_CODE; + ss[0][2].majorRoads |= WEST_CODE | EAST_CODE; + } + if (kwadraty[x][y].jestDroga[NORTH_WEST]) { + ss[2][2].majorRoads |= NORTH_WEST_CODE; + ss[1][3].majorRoads |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + ss[0][4].majorRoads |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + } + + // jest odcinek rzeki na tym kierunku + if (kwadraty[x][y].jestPrzeszkodaWodna[NORTH]) { + ss[2][2].rivers |= NORTH_CODE; + ss[2][3].rivers |= NORTH_CODE | SOUTH_CODE; + ss[2][4].rivers |= NORTH_CODE | SOUTH_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[NORTH_EAST]) { + ss[2][2].rivers |= NORTH_EAST_CODE; + ss[3][3].rivers |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + ss[4][4].rivers |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[EAST]) { + ss[2][2].rivers |= EAST_CODE; + ss[3][2].rivers |= EAST_CODE | WEST_CODE; + ss[4][2].rivers |= EAST_CODE | WEST_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[SOUTH_EAST]) { + ss[2][2].rivers |= SOUTH_EAST_CODE; + ss[3][1].rivers |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + ss[4][0].rivers |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[SOUTH]) { + ss[2][2].rivers |= SOUTH_CODE; + ss[2][1].rivers |= SOUTH_CODE | NORTH_CODE; + ss[2][0].rivers |= SOUTH_CODE | NORTH_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[SOUTH_WEST]) { + ss[2][2].rivers |= SOUTH_WEST_CODE; + ss[1][1].rivers |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + ss[0][0].rivers |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[WEST]) { + ss[2][2].rivers |= WEST_CODE; + ss[1][2].rivers |= WEST_CODE | EAST_CODE; + ss[0][2].rivers |= WEST_CODE | EAST_CODE; + } + if (kwadraty[x][y].jestPrzeszkodaWodna[NORTH_WEST]) { + ss[2][2].rivers |= NORTH_WEST_CODE; + ss[1][3].rivers |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + ss[0][4].rivers |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + } + + // jest odcinek rowu na tym kierunku + if (kwadraty[x][y].jestRow[NORTH]) { + ss[2][2].drains |= NORTH_CODE; + ss[2][3].drains |= NORTH_CODE | SOUTH_CODE; + ss[2][4].drains |= NORTH_CODE | SOUTH_CODE; + } + if (kwadraty[x][y].jestRow[NORTH_EAST]) { + ss[2][2].drains |= NORTH_EAST_CODE; + ss[3][3].drains |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + ss[4][4].drains |= NORTH_EAST_CODE | SOUTH_WEST_CODE; + } + if (kwadraty[x][y].jestRow[EAST]) { + ss[2][2].drains |= EAST_CODE; + ss[3][2].drains |= EAST_CODE | WEST_CODE; + ss[4][2].drains |= EAST_CODE | WEST_CODE; + } + if (kwadraty[x][y].jestRow[SOUTH_EAST]) { + ss[2][2].drains |= SOUTH_EAST_CODE; + ss[3][1].drains |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + ss[4][0].drains |= SOUTH_EAST_CODE | NORTH_WEST_CODE; + } + if (kwadraty[x][y].jestRow[SOUTH]) { + ss[2][2].drains |= SOUTH_CODE; + ss[2][1].drains |= SOUTH_CODE | NORTH_CODE; + ss[2][0].drains |= SOUTH_CODE | NORTH_CODE; + } + if (kwadraty[x][y].jestRow[SOUTH_WEST]) { + ss[2][2].drains |= SOUTH_WEST_CODE; + ss[1][1].drains |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + ss[0][0].drains |= SOUTH_WEST_CODE | NORTH_EAST_CODE; + } + if (kwadraty[x][y].jestRow[WEST]) { + ss[2][2].drains |= WEST_CODE; + ss[1][2].drains |= WEST_CODE | EAST_CODE; + ss[0][2].drains |= WEST_CODE | EAST_CODE; + } + if (kwadraty[x][y].jestRow[NORTH_WEST]) { + ss[2][2].drains |= NORTH_WEST_CODE; + ss[1][3].drains |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + ss[0][4].drains |= NORTH_WEST_CODE | SOUTH_EAST_CODE; + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < m; j++) { + int hex = 0; + if (kwadraty[x][y].stopienZalesienia > 0) { + hex = TerrainType.FOREST.ID; + } else if (kwadraty[x][y].stopienZawodnienia > 0) { + hex = TerrainType.WATER.ID; + } else if (kwadraty[x][y].stopienZabudowy > 0) { + hex = TerrainType.BUILDINGS.ID; + } else if (kwadraty[x][y].stopienZabagnienia > 0) { + hex = TerrainType.SWAMP.ID; + } + ss[i][j].terrainType = (byte) hex; + ss[i][j].elevation = (short) kwadraty[x][y].wysokoscSrednia; + } + } + } + } + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int i = 0; i < m; i++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + for (int j = 0; j < m; j++) { + ss_all[x][y][i][j].save(out); + } + } + } + } + out.close(); + logger.debug("Zapisano nowy plik mapy: " + sb + " dla rozmiaru MK= " + dlmk); + } + + void load(String FName) throws IOException { + try { + fileName = FName; + StringBuilder sb = new StringBuilder(100); + sb.append(MapConsts.KWADRATY_DIR); + sb.append(fileName); + sb.append(".bin"); + SmallSquare ss = new SmallSquare(); + ObjectInputStream in = new ObjectInputStream(new FileInputStream(sb.toString())); + kwadraty = new Kwadrat[MapConsts.SS_PER_BS_X][MapConsts.SS_PER_BS_Y]; + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + kwadraty[x][y] = new Kwadrat(); + ss.load(in); + switch (ss.terrainType) { + case 0: + break; + case 1: + break; + case 4: + break; + case 2: + kwadraty[x][y].stopienZabagnienia = 1.f; + break; + case 3: + kwadraty[x][y].stopienZawodnienia = 1.f; + break; + case 5: + kwadraty[x][y].stopienZabudowy = 1.f; + break; + case 6: + kwadraty[x][y].stopienZalesienia = 1.f; + break; + } + + kwadraty[x][y].wysokoscSrednia = ss.elevation; + kwadraty[x][y].roznicaWzniesien = 0; + + int bit_1 = 1; + int hex = ss.majorRoads; + for (int i = 0; i < kwadraty[x][y].jestDroga.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestDroga[i] = true; + } + bit_1 <<= 1; + } + bit_1 = 1; + hex = ss.rivers; + for (int i = 0; i < kwadraty[x][y].jestPrzeszkodaWodna.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestPrzeszkodaWodna[i] = true; + } + bit_1 <<= 1; + } + bit_1 = 1; + hex = ss.drains; + for (int i = 0; i < kwadraty[x][y].jestRow.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestRow[i] = true; + } + bit_1 <<= 1; + } + kwadraty[x][y].bs = this; + } + } + in.close(); + logger.debug("Doczytano plik mapy: " + sb.toString()); + } catch (IOException e) { + kwadraty = null; + throw e; + } + } + + /** + * Funkcja generuje nowy plik z danymi na podstawie danych z pliku referencyjnego (kwadraty o rozm. 200m). + *

Nowy plik moze byc z danymi w innej skali (kwadraty o rozmiarach: 100m lub 50m) i/lub innym formacie (binarny, tekstowy). + * + * @param dir katalog docelowy dla nowego pliku + * @param dlmk rozmiar kwadratow generownych danych + * @throws IOException generowany wyjątek + */ + public void saveNewFileWithNewScale_old_format(String dir, int dlmk, + boolean zalesienie, boolean zawodnienie, boolean zabudowa, boolean zabagnienie, + boolean wysokosc, boolean roznicaWzniesien, boolean drogi, + boolean rzeki, boolean rowy) throws IOException { + if (MapConsts.DL_MK != 200) { + // operacja tylko dla danych terenowych o kwadratach 200m + return; + } + int m = 1; + String s = ""; + if (dlmk == 200) { + m = 1; + s = "200m/"; + } else if (dlmk == 100) { + m = 2; + s = "100m/"; + } else if (dlmk == 50) { + m = 4; + s = "50m/"; + } else if (dlmk == 25) { + m = 8; + s = "25m/"; + } else { + return; + } + StringBuilder sb = new StringBuilder(100); + sb.append(dir); + sb.append(s); + // Utworzenie katalogów, gdyby nie istniały. + File directory = new File(sb.toString()); + directory.mkdirs(); + sb.append(fileName); + sb.append(".bin"); + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(sb.toString())); + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + for (int k = 0; k < m * m; k++) { + float f; + int hex = 0; + if (zalesienie) { + f = kwadraty[x][y].stopienZalesienia * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + } + out.writeByte(hex); + hex = 0; + if (zawodnienie) { + f = kwadraty[x][y].stopienZawodnienia * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + } + out.writeByte(hex); + hex = 0; + if (zabudowa) { + f = kwadraty[x][y].stopienZabudowy * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + } + out.writeByte(hex); + hex = 0; + if (zabagnienie) { + f = kwadraty[x][y].stopienZabagnienia * 100.0f; + hex = (int) f; + hex = (hex > 100) ? 100 : hex; + hex = (hex < 0) ? 0 : hex; + } + out.writeByte(hex); + if (wysokosc) { + out.writeInt(kwadraty[x][y].wysokoscSrednia); + } else { + out.writeInt(0); + } + if (roznicaWzniesien) { + out.writeInt(kwadraty[x][y].roznicaWzniesien); + } else { + out.writeInt(0); + } + int bit_1; + hex = 0; + if (drogi) { + bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestDroga.length; i++) { + // jest odcinek drogi na tym kierunku + if (kwadraty[x][y].jestDroga[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + } + out.writeByte(hex); + hex = 0; + if (rzeki) { + bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestPrzeszkodaWodna.length; i++) { + // jest odcinek przeszkody wodnej na tym kierunku + if (kwadraty[x][y].jestPrzeszkodaWodna[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + } + out.writeByte(hex); + hex = 0; + if (rowy) { + bit_1 = 1; + for (int i = 0; i < kwadraty[x][y].jestRow.length; i++) { + // jest odcinek rowu na tym kierunku + if (kwadraty[x][y].jestRow[i]) { + hex |= bit_1; + } + bit_1 <<= 1; + } + } + out.writeByte(hex); + } + } + } + out.close(); + logger.debug("Zapisano nowy plik mapy: " + sb.toString() + " dla rozmiaru MK= " + dlmk); + } + + + public RightBigSquare() { + } + + // konstruktor ladujacy duzy kwadrat z pliku binarnego + RightBigSquare(String FName) throws IOException { + try { + fileName = FName; + StringBuilder sb = new StringBuilder(100); + sb.append(MapConsts.KWADRATY_DIR); + sb.append(fileName); + sb.append(".bin"); + ObjectInputStream in = new ObjectInputStream(new FileInputStream(sb.toString())); + kwadraty = new Kwadrat[MapConsts.SS_PER_BS_X][MapConsts.SS_PER_BS_Y]; + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + kwadraty[x][y] = new Kwadrat(); + int hex = in.readByte(); + kwadraty[x][y].stopienZalesienia = (float) hex * (1.0f / 100.f); + hex = in.readByte(); + kwadraty[x][y].stopienZawodnienia = (float) hex * (1.0f / 100.f); + hex = in.readByte(); + kwadraty[x][y].stopienZabudowy = (float) hex * (1.0f / 100.f); + hex = in.readByte(); + kwadraty[x][y].stopienZabagnienia = (float) hex * (1.0f / 100.f); + kwadraty[x][y].wysokoscSrednia = in.readInt(); + kwadraty[x][y].roznicaWzniesien = in.readInt(); + int bit_1 = 1; + hex = in.readByte(); + for (int i = 0; i < kwadraty[x][y].jestDroga.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestDroga[i] = true; + } + bit_1 <<= 1; + } + bit_1 = 1; + hex = in.readByte(); + for (int i = 0; i < kwadraty[x][y].jestPrzeszkodaWodna.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestPrzeszkodaWodna[i] = true; + } + bit_1 <<= 1; + } + bit_1 = 1; + hex = in.readByte(); + for (int i = 0; i < kwadraty[x][y].jestRow.length; i++) { + // jest odcinek rowu na tym kierunku + if ((hex & bit_1) != 0) { + kwadraty[x][y].jestRow[i] = true; + } + bit_1 <<= 1; + } + kwadraty[x][y].bs = this; + } + } + in.close(); + logger.debug("Doczytano plik mapy: " + sb.toString()); + } catch (IOException e) { + kwadraty = null; + throw e; + } + } + + void resetSquares(boolean zalesienie, boolean zawodnienie, boolean zabudowa, boolean zabagnienie, + boolean wysokosc, boolean roznicaWzniesien, boolean drogi, boolean rzeki, boolean rowy) { + for (int x = 0; x < MapConsts.SS_PER_BS_X; x++) { + for (int y = 0; y < MapConsts.SS_PER_BS_Y; y++) { + kwadraty[x][y].stopienZalesienia = (zalesienie) ? 0 : kwadraty[x][y].stopienZalesienia; + kwadraty[x][y].stopienZawodnienia = (zawodnienie) ? 0 : kwadraty[x][y].stopienZawodnienia; + kwadraty[x][y].stopienZabudowy = (zabudowa) ? 0 : kwadraty[x][y].stopienZabudowy; + kwadraty[x][y].stopienZabagnienia = (zabagnienie) ? 0 : kwadraty[x][y].stopienZabagnienia; + kwadraty[x][y].wysokoscSrednia = (wysokosc) ? 0 : kwadraty[x][y].wysokoscSrednia; + kwadraty[x][y].roznicaWzniesien = (roznicaWzniesien) ? 0 : kwadraty[x][y].roznicaWzniesien; + if (drogi) { + for (int i = 0; i < kwadraty[x][y].jestDroga.length; i++) { + kwadraty[x][y].jestDroga[i] = false; + } + } + if (rzeki) { + for (int i = 0; i < kwadraty[x][y].jestPrzeszkodaWodna.length; i++) { + kwadraty[x][y].jestPrzeszkodaWodna[i] = false; + } + } + if (rowy) { + for (int i = 0; i < kwadraty[x][y].jestRow.length; i++) { + kwadraty[x][y].jestRow[i] = false; + } + } + } + } + } + + @Override + public String toString() { + return "RightBigSquare{" + this.fileName + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/SiecDrogowa.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/SiecDrogowa.java new file mode 100644 index 0000000..e46eae4 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/SiecDrogowa.java @@ -0,0 +1,364 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.common.EGeoDirection; + +public class SiecDrogowa { + + static final Logger logger = Logger.getLogger(SiecDrogowa.class); + + ArrayList luki; + ArrayList wezly; + static SiecDrogowa instancja; + static String path; + + Arkusz[][] arkusze; + + public int liczbaZmian = 0; + static final int LICZBA_ZMIAN = 50; + + SiecDrogowa(String dataPath) { + instancja = this; +// File wd = new File("."); +// PATH = wd.getPath(); + path = dataPath; + this.odczytajWezly(path + "/nodes.txt"); + logger.debug("wczytano " + this.wezly.size() + " wezlow."); + this.odczytajLuki(path + "/arcs.txt"); + logger.debug("wczytano " + this.luki.size() + " lukow."); + stworzArkusze(); + + +// logger.debug("Liczba lukow w arkuszach:"); +// int ll = 0; +// for (int i = 0; i < Arkusz.lx; i++) { +// for (int j = 0; j < Arkusz.ly; j++) { +// logger.debug(String.format("(%1$2d, %2$2d) - %3$6d", i, j, this.arkusze[i][j].luki.size())); +// ll += this.arkusze[i][j].luki.size(); +// } +// } +// logger.debug("Sum. liczba lukow w arkuszach:" + ll); + } + + void odczytajWezly(String fname) { + try { + FileReader fis = new FileReader(fname); + BufferedReader br = new BufferedReader(fis); + if (br.ready()) { + String line = br.readLine().trim(); + int licz = Integer.parseInt(line); + this.wezly = new ArrayList(licz);// + 1000); + while (null != (line = br.readLine())) { + WezelDrogowy nowyWezel = new WezelDrogowy(line); + nowyWezel.id = this.wezly.size(); //Indeksowanie od 0 + this.wezly.add(nowyWezel); + } + } + br.close(); + } catch (IOException e) { + logger.warn("Brak pliku z wezlami: " + fname); + } + } + + void odczytajLuki(String fname) { + try { + FileReader fis = new FileReader(fname); + BufferedReader br = new BufferedReader(fis); + if (br.ready()) { + String line = br.readLine().trim(); + int licz = Integer.parseInt(line); + this.luki = new ArrayList(licz);// + 1000); + while (null != (line = br.readLine())) { + LukDrogowy nowyLuk = new LukDrogowy(line); + nowyLuk.id = this.luki.size(); //Indeksowanie od 0 + this.luki.add(nowyLuk); + } + } + br.close(); + } catch (IOException e) { + logger.warn("Brak pliku z lukami: " + fname); + } + } + + void stworzArkusze() { + // wyznaczenie wymiarow pola walki w malych kwadratach + Arkusz.lx = MapConsts.getDX_REF() * MapConsts.BS_PER_DEG_X * MapConsts.SS_PER_BS_X; + Arkusz.ly = MapConsts.getDY_REF() * MapConsts.BS_PER_DEG_Y * MapConsts.SS_PER_BS_Y; + // wyznacznie liczby arkuszy mapy w obu wymiarach + Arkusz.lx = Arkusz.lx / MapConsts.SS_PER_SHEET + 1; + Arkusz.ly = Arkusz.ly / MapConsts.SS_PER_SHEET + 1; + this.arkusze = new Arkusz[Arkusz.lx][Arkusz.ly]; + for (int i = 0; i < Arkusz.lx; i++) { + for (int j = 0; j < Arkusz.ly; j++) { + this.arkusze[i][j] = new Arkusz(); + } + } + // wspolrzedne arkusza mapy + int xa, ya; + for (int i = 0; i < this.wezly.size(); i++) { + WezelDrogowy wezel = this.wezly.get(i); + xa = wezel.idKw.x / MapConsts.SS_PER_SHEET; + ya = wezel.idKw.y / MapConsts.SS_PER_SHEET; + this.arkusze[xa][ya].wezly.add(wezel); + } + int xa2, ya2; + for (int i = 0; i < this.luki.size(); i++) { + LukDrogowy luk = this.luki.get(i); + xa = luk.getWezly()[0].idKw.x / MapConsts.SS_PER_SHEET; + ya = luk.getWezly()[0].idKw.y / MapConsts.SS_PER_SHEET; + xa2 = luk.getWezly()[1].idKw.x / MapConsts.SS_PER_SHEET; + ya2 = luk.getWezly()[1].idKw.y / MapConsts.SS_PER_SHEET; + boolean b = Arkusz.mniejszeWspolrzedne(xa, ya, xa2, ya2); + if (b) { + this.arkusze[xa][ya].luki.add(luk); + } else { + this.arkusze[xa2][ya2].luki.add(luk); + } + } + } + + public static void utworzSiecDrogowa(){ +// if (null == instancja){ +// instancja = new SiecDrogowa(MapConsts.DROGI_DIR); +// } + } + + public static LukDrogowy dodajLuk(WezelDrogowy wezel1, WezelDrogowy wezel2) { + utworzSiecDrogowa(); + LukDrogowy nowyLuk = new LukDrogowy(); + nowyLuk.getWezly()[0] = wezel1; + wezel1.luki.add(nowyLuk); + nowyLuk.getWezly()[1] = wezel2; + wezel2.luki.add(nowyLuk); + instancja.luki.add(nowyLuk); + nowyLuk.id = instancja.luki.size() - 1; + return nowyLuk; + } + + public static WezelDrogowy dodajWezel(String wspUTM) { + utworzSiecDrogowa(); + // TODO dodac weryfikacje poprawnosci wspolrzednych UTM + WezelDrogowy nowyWezel = new WezelDrogowy(); + nowyWezel.setXms(Teren.zamienWspUtmNaWspXms(wspUTM.substring(0, 6))); + nowyWezel.setYms(Teren.zamienWspUtmNaWspXms(wspUTM.substring(8, 14))); + int x = GridCoord.zamienWspXmsNaIdKwadratuX(nowyWezel.getXms()); + int y = GridCoord.zamienWspYmsNaIdKwadratuY(nowyWezel.getYms()); + nowyWezel.idKw = new GridCoord(x, y); + nowyWezel.luki = new ArrayList(); + instancja.wezly.add(nowyWezel); + nowyWezel.id = instancja.wezly.size() - 1; + return nowyWezel; + } + + public static void usunLuk(LukDrogowy usuwanyLuk) { + utworzSiecDrogowa(); + if (usuwanyLuk.id < 0 || usuwanyLuk.id >= instancja.luki.size()) { + return; + } + int ostatniId = instancja.luki.size() - 1; + LukDrogowy lukOstatni = instancja.luki.get(ostatniId); + lukOstatni.id = usuwanyLuk.id; + instancja.luki.set(lukOstatni.id, lukOstatni); + instancja.luki.remove(ostatniId); + // aktualizacja wezlow luku + usuwanyLuk.getWezly()[0].luki.remove(usuwanyLuk); + usuwanyLuk.getWezly()[1].luki.remove(usuwanyLuk); + if (usuwanyLuk.getWezly()[0].luki.size() == 0) { + usunWezel(usuwanyLuk.getWezly()[0]); + } + if (usuwanyLuk.getWezly()[1].luki.size() == 0) { + usunWezel(usuwanyLuk.getWezly()[1]); + } + } + + public static void usunWezel(WezelDrogowy usuwanyWezel) { + utworzSiecDrogowa(); + if (usuwanyWezel.id < 0 || usuwanyWezel.id >= instancja.wezly.size()) { + return; + } + int ostatniId = instancja.wezly.size() - 1; + WezelDrogowy wezelOstatni = instancja.wezly.get(ostatniId); + wezelOstatni.id = usuwanyWezel.id; + instancja.wezly.set(wezelOstatni.id, wezelOstatni); + instancja.wezly.remove(ostatniId); + } + + public static void wpiszDrogiWKwadraty() { + utworzSiecDrogowa(); + GridCoord[] kwadratyOdcinka; + for (int i = 0; i < instancja.luki.size(); i++) { + LukDrogowy luk = instancja.luki.get(i); + GridCoord kw1 = luk.getWezly()[0].idKw; + GridCoord kw2 = luk.getWezly()[1].idKw; + kwadratyOdcinka = GeomUtils.kwadratyOdcinka(kw1, kw2); + wpiszOdcinekDrogiWKwadraty(kwadratyOdcinka); + } + Teren.zapisBuforaMapyDoPliku(); + } + + static void wpiszOdcinekDrogiWKwadraty(GridCoord[] kwadratyOdcinka) { + if (null == kwadratyOdcinka) { + return; + } + for (int i = 0; i < kwadratyOdcinka.length - 1; i++) { + GridCoord idkw1 = kwadratyOdcinka[i]; + GridCoord idkw2 = kwadratyOdcinka[i + 1]; + EGeoDirection kier = GeomUtils.kierunekDlaSasiada(idkw1, idkw2); + Kwadrat kw = Teren.getKwadrat(idkw1.x, idkw1.y); + if (null != kw) { + kw.setJestDroga(kier, true); + } + kw = Teren.getKwadrat(idkw2.x, idkw2.y); + if (null != kw) { + // wyznaczam kierunek przeciwny + kier = kier.oppositeDirect(); + kw.setJestDroga(kier, true); + } + } + } + + public void aktualizujPliki(boolean wymuszony) throws IOException { + if (wymuszony) { + if (this.liczbaZmian == 0) + // nie bylo modyfikacji od ostatniej zapisu + return; + this.liczbaZmian = -1; + } + this.liczbaZmian++; + this.liczbaZmian %= LICZBA_ZMIAN; + if (0 == this.liczbaZmian) { + try { + // zapisanie wezlow + BufferedWriter bw = new BufferedWriter(new FileWriter(path + "\\nodes.txt")); + String linia = Integer.toString(this.wezly.size()); + bw.write(linia, 0, linia.length()); + bw.newLine(); + String s; + for (int i = 0; i < this.wezly.size(); i++) { + WezelDrogowy wezel = this.wezly.get(i); + s = String.format("%07d", wezel.id); + linia = s; + linia += " "; + s = String.format("%010d", wezel.getXms()); + linia += s; + linia += " "; + s = String.format("%010d", wezel.getYms()); + linia += s; + linia += " "; + int most = 0; + if (wezel.jestMostem) { + most = 1; + } + s = String.format("%01d", most); + linia += s; + bw.write(linia, 0, linia.length()); + bw.newLine(); + } + bw.close(); + logger.info("Zapisano plik wezlow sieci drogowej: " + path + "\\nodes.txt"); + } catch (IOException e) { + logger.warn("Blad zapisu pliku wezlow sieci drogowej: " + path + "\\nodes.txt"); + } + try { + // zapisanie lukow + BufferedWriter bw = new BufferedWriter(new FileWriter(path + "\\arcs.txt")); + String linia = Integer.toString(this.luki.size()); + bw.write(linia, 0, linia.length()); + bw.newLine(); + String s; + for (int i = 0; i < this.luki.size(); i++) { + LukDrogowy luk = this.luki.get(i); + s = String.format("%07d", luk.getWezly()[0].id); + linia = s; + linia += " "; + s = String.format("%07d", luk.getWezly()[1].id); + linia += s; + linia += " "; + s = String.format("%05d", luk.dlugosc); + linia += s; + linia += " "; + s = String.format("%02d", luk.szerokosc); + linia += s; + linia += " "; + int most = 0; + if (luk.jestMostem) { + most = 1; + } + s = String.format("%01d", most); + linia += s; + bw.write(linia, 0, linia.length()); + bw.newLine(); + } + bw.close(); + logger.info("Zapisano plik lukow sieci drogowej: " + path + "\\arcs.txt"); + } catch (IOException e) { + logger.warn("Blad zapisu pliku lukow sieci drogowej: " + path + "\\arcs.txt"); + } + } + } + + // funkcja zwraca kolekcje lukow wewnatrz prostokata + // zawartego miedzy lewym dolnym i prawym gornym punktem + public static ArrayList dajLukiWObszarze(String wspUtmLD, String wspUtmPG) { + utworzSiecDrogowa(); + int xms = Teren.zamienWspUtmNaWspXms(wspUtmLD); + int yms = Teren.zamienWspUtmNaWspYms(wspUtmLD); + int xa_ld = GridCoord.zamienWspXmsNaIdKwadratuX(xms); + xa_ld /= MapConsts.SS_PER_SHEET; + int ya_ld = GridCoord.zamienWspYmsNaIdKwadratuY(yms); + ya_ld /= MapConsts.SS_PER_SHEET; + + xms = Teren.zamienWspUtmNaWspXms(wspUtmPG); + yms = Teren.zamienWspUtmNaWspYms(wspUtmPG); + int xa_pg = GridCoord.zamienWspXmsNaIdKwadratuX(xms); + xa_pg /= MapConsts.SS_PER_SHEET; + int ya_pg = GridCoord.zamienWspYmsNaIdKwadratuY(yms); + ya_pg /= MapConsts.SS_PER_SHEET; + + //if (xa_ld > xa_pg || ya_ld > ya_pg) { + // return null; + //} + if (xa_ld > xa_pg){ + int tmp = xa_ld; + xa_ld = xa_pg; + xa_pg = tmp; + } + if (ya_ld > ya_pg){ + int tmp = ya_ld; + ya_ld = ya_pg; + ya_pg = tmp; + } + ArrayList luki = new ArrayList(); + // wspolrzedne arkusza mapy + for (int xa = xa_ld; xa <= xa_pg; xa++) { + for (int ya = ya_ld; ya <= ya_pg; ya++) { + if (instancja.arkusze[xa][ya].luki != null) { + luki.addAll(instancja.arkusze[xa][ya].luki); + } + } + } + return luki; + } + + public static void main(String[] args) { +// GridCoord idKw = GridCoord.zamienWspUtmNaIdKwadratu("520101N0160202E"); +// String utm = GridCoord.zamienIdKwadratuNaWspUtm(idKw); +// int xms = GridCoord.zamienIdKwadratuXNaWspXms(idKw.x); +// int yms = GridCoord.zamienIdKwadratuYNaWspYms(idKw.y); +// utm = Teren.zamienWspXmsYmsNaWspUtm(xms, yms); +// float lon = GridCoord.zamienIdKwadratuXNaDlugoscGeo(idKw.x); +// float lat = GridCoord.zamienIdKwadratuYNaSzerokoscGeo(idKw.y); +// idKw.x = GridCoord.zamienDlugoscGeoNaIdKwadratuX(lon); +// idKw.y = GridCoord.zamienSzerokoscGeoNaIdKwadratuY(lat); + utworzSiecDrogowa(); + SiecDrogowa.wpiszDrogiWKwadraty(); + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/SmallSquare.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/SmallSquare.java new file mode 100644 index 0000000..ae90151 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/SmallSquare.java @@ -0,0 +1,83 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class SmallSquare { + + + /** + * The height above the level of the sea. + */ + short elevation; + /** + * 0 - BARE_GROUND + * 1 - GRASS + * 2 - SWAMP + * 3 - WATER + * 4 - SCRUB, BUSHES + * 5 - BUILDINGS + * 6 - FOREST + */ + byte terrainType; + /** + * Rodzaj drogi na danym kierunku. + * 0 - no road, 1 - small roads, 2 - minor roads, 3 - major roads + */ +// byte[] roads; + byte smallRoads; + byte minorRoads; + byte majorRoads; + + /** + * Rodzaj przeszkody wodnej na danym kierunku. + * 0 - no watercourse, 1 - drain, ditch, 2 - canal, stream, 3 - river + */ +// byte[] waterWays; + byte drains; + byte streams; + byte rivers; + + + public void reset() { + elevation = 0; + terrainType = 0; + smallRoads = 0; + minorRoads = 0; + majorRoads = 0; + drains = 0; + streams = 0; + rivers = 0; + } + + public void save(ObjectOutputStream out) throws IOException { + out.writeShort(elevation); + out.writeByte(terrainType); + out.writeByte(smallRoads); + out.writeByte(minorRoads); + out.writeByte(majorRoads); + out.writeByte(drains); + out.writeByte(streams); + out.writeByte(rivers); + } + + public void load(ObjectInputStream in) throws IOException { + elevation = in.readShort(); + terrainType = in.readByte(); + smallRoads = in.readByte(); + minorRoads = in.readByte(); + majorRoads = in.readByte(); + drains = in.readByte(); + streams = in.readByte(); + rivers = in.readByte(); + } + + public SmallSquare(short elevation, byte terrainType) { + this.elevation = elevation; + this.terrainType = terrainType; + } + + public SmallSquare() { + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/Teren.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/Teren.java new file mode 100644 index 0000000..2478285 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/Teren.java @@ -0,0 +1,1103 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.io.*; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.common.EGeoDirection; +import pl.wat.ms4ds.common.ERodzajPodwozia; +import pl.wat.ms4ds.common.ERodzajTerenuPokrycie; +import pl.wat.ms4ds.terenfunkcje.nmt.NMTDataProvider; + +import static pl.wat.ms4ds.terenfunkcje.Kwadrat.EMPTY_SQUARE; + +public class Teren { + + private static final Logger LOGGER = Logger.getLogger(Teren.class); + + private static final int BIG_X_MAX = 100; + private static final int BIG_Y_MAX = 100; + + private static BigSquare bigSquares[][] = new BigSquare[BIG_X_MAX][BIG_Y_MAX]; + + // tablica obiektów synchronizujących dostęp do dużych kwadratów przy odczycie z pliku + private static Object bsSynch[][] = new Object[BIG_X_MAX][BIG_Y_MAX]; + + private static final float[][] STOPIEN_PRZEJEZDNOSCI; + + private static final String LITERALS = "ABCDEF"; + + /** + * Nazwa pliku z konfiguracja mechanizmu odpowiedzialnego za transfer. Plik + * musi znajdowac sie w katalogu glownym aplikacji, ewentualnie musi tu byc + * podana sciezka bezwzgledna do niego. + */ + private static final String PLIK_Z_USTAWIENIAMI = "teren.properties"; + + private static boolean przejezdnoscZawsze; + static double minStopienPrzejezd; + static double minStopienPrzejezdNaPrzelaj; + + private static double minStopienPrzejezdNaDrodzeNachylenie; + private static double minStopienPrzejezdNaPrzelajNachylenie; + + private static double minKatNachylTerenuNaDrodze; + private static double maxKatNachylTerenuNaDrodze; + private static double minKatNachylTerenuNaPrzelaj; + private static double maxKatNachylTerenuNaPrzelaj; + + /** + * Jawne wywolanie zapisu do pliku bufora zmian terenu + */ + public static void zapisBuforaMapyDoPliku() { + for (int i = 0; i < bigSquares.length; i++) { + for (int j = 0; j < bigSquares[i].length; j++) { + BigSquare bs = bigSquares[i][j]; + if (null != bs && bs instanceof RightBigSquare) { + RightBigSquare rbs = (RightBigSquare) bigSquares[i][j]; + try { + rbs.liczbaZmian++; + rbs.updateFile(true, null); + } catch (IOException e) { + LOGGER.warn("Błąd zapisu pliku mapy: " + rbs.fileName); + } + } + } + } + } + + public static void reset() { + for (int i = 0; i < bigSquares.length; i++) { + for (int j = 0; j < bigSquares[i].length; j++) { + bigSquares[i][j] = null; + } + } + bigSquaresInMemory = 0; + for (int i = 0; i < history.length; i++) { + history[i].set(-1, -1); + } + System.gc(); + } + + // funkcja zwraca reprezentacje danej wspolrzednej UTM w milisekundach + public static int zamienWspUtmNaWspXms(String wspolrzedneUtm) { + String d = wspolrzedneUtm.substring(8, 10); + int ms = Integer.parseInt(d) * MapConsts.DEG_MS; + d = wspolrzedneUtm.substring(10, 12); + ms += Integer.parseInt(d) * 60000; + d = wspolrzedneUtm.substring(12, 14); + ms += Integer.parseInt(d) * 1000; + if (wspolrzedneUtm.charAt(14) == 'W') { + ms = -ms; + } + ms += 180 * MapConsts.DEG_MS; + return ms; + } + + // funkcja zwraca reprezentacje danej wspolrzednej UTM w milisekundach + public static int zamienWspUtmNaWspYms(String wspolrzedneUtm) { + String d = wspolrzedneUtm.substring(0, 2); + int ms = Integer.parseInt(d) * MapConsts.DEG_MS; + d = wspolrzedneUtm.substring(2, 4); + ms += Integer.parseInt(d) * 60000; + d = wspolrzedneUtm.substring(4, 6); + ms += Integer.parseInt(d) * 1000; + if (wspolrzedneUtm.charAt(6) == 'S') { + ms = -ms; + } + ms += 90 * MapConsts.DEG_MS; + return ms; + } + + public static String zamienWspXmsYmsNaWspUtm(long xms, long yms) { + char cx = 'E'; + // stopnie dlugosci geograficznej + long x_st = xms / MapConsts.DEG_MS - 180; + if (x_st < 0) { + x_st = -x_st; + cx = 'W'; + } + long reszta = xms % MapConsts.DEG_MS; + // minuty dlugosci geograficznej + long x_m = reszta / 60000; + // sekundy dlugosci geograficznej + long x_s = reszta % 60000; + x_s /= 1000; + + char cy = 'N'; + // stopnie szerokosci geograficznej + long y_st = yms / MapConsts.DEG_MS - 90; + if (y_st < 0) { + y_st = -y_st; + cy = 'S'; + } + reszta = yms % MapConsts.DEG_MS; + // minuty szerokosci geograficznej + long y_m = reszta / 60000; + // sekundy szerokosci geograficznej + long y_s = reszta % 60000; + y_s /= 1000; + + return String.format( + "%1$02d%2$02d%3$02d%4$c%5$03d%6$02d%7$02d%8$c", y_st, y_m, y_s, cy, x_st, x_m, x_s, cx); + } + + // funkcja wyznacza wspolrzedne UTM ze wspolrzednych dziesietnych + // Zalozenie: wspolrzedne w zakresie dlugosc wschodnia do 100 stopni, szerokosc polnocna + public static String zamienWspGeoDziesietnieNaWspUtm(float lon, float lat) { + int x_st = (int) lon; + int y_st = (int) lat; + + float s = (lon - x_st) * 3600; + int x_s = (int) s; + int x_m = x_s / 60; + x_s = x_s % 60; + + s = (lat - y_st) * 3600; + int y_s = (int) s; + int y_m = y_s / 60; + y_s = y_s % 60; + + return String.format("%1$02d%2$02d%3$02dN%4$03d%5$02d%6$02dE", y_st, y_m, y_s, x_st, x_m, x_s); + } + + /** + * Zwraca nazwę pliku na podstawie współrzędnych geograficznych. + * + * @param lat szerokość geograficzna + * @param lon długość geograficzna + * @return Nazwa zwracanego pliku z danymi (null - gdy niepoprawne współrzędne). + */ + public static String getFileName(double lat, double lon) { + int idX = GridCoord.zamienDlugoscGeoNaIdKwadratuX(lon); + int idY = GridCoord.zamienSzerokoscGeoNaIdKwadratuY(lat); + int bigX = idX / MapConsts.SS_PER_BS_X; + int bigY = idY / MapConsts.SS_PER_BS_Y; + return getFileName(bigX, bigY); + } + + private static String getFileName(int bsX, int bsY) { + if ((bsX < 0) || (bsY < 0)) { + return null; + } + int x_stop = MapConsts.X_REF + bsX / MapConsts.BS_PER_DEG_X - 180; + char cLon = (x_stop < 0) ? 'W' : 'E'; + if (x_stop < 0) { + x_stop = -x_stop; + } + int dx = bsX % MapConsts.BS_PER_DEG_X; + char cx = LITERALS.charAt(dx); + + int y_stop = MapConsts.Y_REF + bsY / MapConsts.BS_PER_DEG_Y - 90; + char cLat = (y_stop < 0) ? 'S' : 'N'; + if (y_stop < 0) { + y_stop = -y_stop; + } + int dy = bsY % MapConsts.BS_PER_DEG_Y; + char cy = LITERALS.charAt(dy); + // przykładowa nazwa pliku: E024B_N50F + + StringBuilder sb = new StringBuilder(); +// sb.append(MapConsts.KWADRATY_DIR); + sb.append(cLon); + if (x_stop < 100) { + sb.append('0'); + } + if (x_stop < 10) { + sb.append('0'); + } + sb.append(x_stop); + sb.append(cx); + sb.append('_'); + sb.append(cLat); + if (y_stop < 10) { + sb.append('0'); + } + sb.append(y_stop); + sb.append(cy); + return sb.toString(); + } + + private static BigSquare loadArea(int bsX, int bsY) { + String fName = getFileName(bsX, bsY); + try { + return new RightBigSquare(fName); + } catch (IOException e) { + LOGGER.warn("Brak pliku mapy: " + MapConsts.KWADRATY_DIR + fName + ".bin"); + return EmptyBigSquare.EMPTY_BIG_SQUARE; + } + } + + private static BigSquare loadAreaNew(int bsX, int bsY) { + String fName = getFileName(bsX, bsY); + try { + RightBigSquare bs = new RightBigSquare(); + bs.load(fName); + return bs; + } catch (IOException e) { + LOGGER.warn("Brak pliku mapy: " + MapConsts.KWADRATY_DIR + fName + ".bin"); + return EmptyBigSquare.EMPTY_BIG_SQUARE; + } + } + + private static ReentrantLock synchr; + + public static Kwadrat getKwadrat(int idX, int idY) { + if (idX < 0 || idY < 0) { + return EMPTY_SQUARE; + } + // wspolrzędna x dużego kwadratu + int bsX = idX / MapConsts.SS_PER_BS_X; + // wspolrzędna y dużego kwadratu + int bsY = idY / MapConsts.SS_PER_BS_Y; + if (bsX < 0 || bsX >= BIG_X_MAX || bsY < 0 || bsY >= BIG_Y_MAX) { + return EMPTY_SQUARE; + } + // wspolrzędna x małego kwadratu w ramach dużego kwadratu + int ssX = idX % MapConsts.SS_PER_BS_X; + // wspolrzędna y małego kwadratu w ramach dużego kwadratu + int ssY = idY % MapConsts.SS_PER_BS_Y; + synchronized (bsSynch[bsX][bsY]) { + if (bigSquares[bsX][bsY] == null) { + makeRoom(bsX, bsY); + bigSquares[bsX][bsY] = loadAreaNew(bsX, bsY); + } + } + return bigSquares[bsX][bsY].getKwadrat(ssX, ssY); + } + + public static Kwadrat getKwadrat2(int idX, int idY) { + if (idX < 0 || idY < 0) { + return EMPTY_SQUARE; + } + // wspolrzędna x dużego kwadratu + int bsX = idX / MapConsts.SS_PER_BS_X; + // wspolrzędna y dużego kwadratu + int bsY = idY / MapConsts.SS_PER_BS_Y; + if (bsX < 0 || bsX >= BIG_X_MAX || bsY < 0 || bsY >= BIG_Y_MAX) { + return EMPTY_SQUARE; + } + // wspolrzędna x małego kwadratu w ramach dużego kwadratu + int ssX = idX % MapConsts.SS_PER_BS_X; + // wspolrzędna y małego kwadratu w ramach dużego kwadratu + int ssY = idY % MapConsts.SS_PER_BS_Y; + if (bigSquares[bsX][bsY] == null) { + makeRoom(bsX, bsY); + bigSquares[bsX][bsY] = loadAreaNew(bsX, bsY); + } + return bigSquares[bsX][bsY].getKwadrat(ssX, ssY); + } + + private static GridCoord[] history = new GridCoord[MapConsts.MAX_BIG_SQUARES_IN_MEMORY]; + private static int bigSquaresInMemory = 0; + + 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) { + RightBigSquare rbs = (RightBigSquare) bigSquares[history[0].x][history[0].y]; + try { + //!! dla potrzeb generowania danych + rbs.liczbaZmian = 1; + rbs.updateFile(true, null); + } catch (IOException e) { + e.printStackTrace(); + } + } + // 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++; + } + } + + public static Kwadrat getKwadrat(String wspUtm) { + int idX = GridCoord.zamienWspUtmNaIdkwadratuX(wspUtm); + int idY = GridCoord.zamienWspUtmNaIdkwadratuY(wspUtm); + return getKwadrat(idX, idY); + } + + public static double getStopienPrzejezdnosci(int x, int y, ERodzajPodwozia podwozie) { + Kwadrat kwd = Teren.getKwadrat(x, y); + if (kwd == EMPTY_SQUARE) { + return 0.0f; + } + if (kwd.getStopienZabudowy() > 0.0f) { + return Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id]; + } + if (kwd.getStopienZawodnienia() > 0.9f) { + return 0.0f; + } + double pow_czysta_start = 1 - kwd.getStopienZalesienia() + - kwd.getStopienZabagnienia() - kwd.getStopienZawodnienia() + - kwd.getStopienZabudowy(); + double sp = pow_czysta_start + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id]; + sp += kwd.getStopienZalesienia() + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id]; + sp += kwd.getStopienZabagnienia() + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id]; + sp += kwd.getStopienZawodnienia() + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id]; + sp *= (1.0f - kwd.getStopienPofaldowania()); + return sp; + } + + public static double getStopienPrzejezdnosci(GridCoord id, ERodzajPodwozia podwozie) { + return Teren.getStopienPrzejezdnosci(id.x, id.y, podwozie); + } + + /** + * @param idKwStart kwadrat startowy + * @param idKwStop kwadrat docelowy + * @param podwozie rodzaj podwozia + * @return stopien przejezdnosci pomieszy dwoma kwadratami + */ + public static double getStopienPrzejezdnosci(GridCoord idKwStart, GridCoord idKwStop, EGeoDirection zKierunku, + ERodzajPodwozia podwozie) { + return getStopienPrzejezdnosci(idKwStart.x, idKwStart.y, idKwStop.x, idKwStop.y, zKierunku, podwozie); + } + + /** + * Tablica danych o braku rowów w kwadracie dla funkcji przejezdnosci, gdy nie uwzględniamy rowów. + */ + private static final boolean[] ROWY_DLA_PRZEJEZDNOSCI = new boolean[8]; + + private static final double TANG_ALFA_MAX_NA_DRODZE; + private static final double TANG_ALFA_MIN_NA_DRODZE; + + private static double a1; + private static double b1; + + /** + * Funkcja opisujaca zmianę stopnia przejezdności podczas ruchu na drodze dla kątów w przedziale <22.5, 45> + * + * @param tangKataNachyl tangens kąta nachylenia zbocza + * @return stopień przejezdności z punktu widzenia nachylenia terenu przy ruchu na drodze + */ + private static double stopienPrzejezdNaDrodzeNachylenie(double tangKataNachyl) { + if (tangKataNachyl <= TANG_ALFA_MIN_NA_DRODZE) { + return 1.0; + } + double st = tangKataNachyl * a1 + b1; + return (st < minStopienPrzejezdNaDrodzeNachylenie) ? minStopienPrzejezdNaDrodzeNachylenie : st; + } + + private static final double TANG_ALFA_MAX_NA_PRZELAJ; + private static final double TANG_ALFA_MIN_NA_PRZELAJ; + + private static double a2; + private static double b2; + + /** + * Funkcja opisujaca zmianę stopnia przejezdności podczas ruchu na przełaj dla kątów w przedziale <22.5, 45> + * + * @param tangKataNachyl tangens kąta nachylenia zbocza + * @return stopień przejezdności z punktu widzenia nachylenia terenu przy ruchu na przełaj + */ + private static double stopienPrzejezdNaPrzelajNachylenie(double tangKataNachyl) { + if (tangKataNachyl <= TANG_ALFA_MIN_NA_PRZELAJ) { + return 1.0; + } + if (tangKataNachyl >= TANG_ALFA_MAX_NA_PRZELAJ || tangKataNachyl <= -TANG_ALFA_MAX_NA_PRZELAJ) { + // brak przejezdnosci z powodu nachylenia + return 0.0; + } + double st = tangKataNachyl * a2 + b2; + return (st < minStopienPrzejezdNaPrzelajNachylenie) ? minStopienPrzejezdNaPrzelajNachylenie : st; + } + + /** + * Funkcja wyznacza stopień przejezdności między dwoma sąsiednimi kwadratami + * uwzględniając warunki terenowe oraz możliwości sprzętu w zakresie pokonywania terenu + * bez możliwości pokonywania rzek i rowów. + * + * @param xStart współrzędna GridCoord.X kwadratu startowego + * @param yStart współrzędna GridCoord.Y kwadratu startowego + * @param xStop współrzędna GridCoord.X kwadratu docelowego + * @param yStop współrzędna GridCoord.Y kwadratu docelowego + * @param zKierunku parametr informujący, z którego kierunku znaleziono się w kwadracie startowym + * @param podwozie rodzaj podwozia + * @return stopien przejezdnosci pomiędzy dwoma sąsiednimi kwadratami + */ + public static double getStopienPrzejezdnosci(int xStart, int yStart, int xStop, int yStop, EGeoDirection zKierunku, + ERodzajPodwozia podwozie) { + if (podwozie == ERodzajPodwozia.NIEOKRESLONE) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + if (xStart == xStop && yStart == yStop) { + return getStopienPrzejezdnosci(xStart, yStart, podwozie); + } + Kwadrat kwStart = Teren.getKwadrat(xStart, yStart); + Kwadrat kwStop = Teren.getKwadrat(xStop, yStop); + if (kwStart == EMPTY_SQUARE || kwStop == EMPTY_SQUARE) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + EGeoDirection nowyKierunek = GeomUtils.kierunekDlaSasiada(xStart, yStart, xStop, yStop); + if (EGeoDirection.UNDEFINED == nowyKierunek) { + LOGGER.warn("Błędna wartość kierunku przy wyznaczaniu stopnia przejezdności."); + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + int roznicaWysokosci = kwStop.wysokoscSrednia - kwStart.wysokoscSrednia; + double tangKataNachyl = roznicaWysokosci / GeomUtils.odlegloscKwadratowSasiednich(nowyKierunek); + boolean jestPrzeszkoda = Teren.jestPrzeszkodaNaKierunkuRuchu(xStart, yStart, zKierunku, nowyKierunek); + if (jestPrzeszkoda) { + if (kwStart.jestDroga[zKierunku.oppositeDirectId] && kwStart.jestDroga[nowyKierunek.id]) { + // sprawdzam, czy istnieje droga z kierunku wejsciowego (kierunek przeciwny do zKierunku) + // do wyjściowego (nowyKierunek), zatem istnieje most + return stopienPrzejezdNaDrodzeNachylenie(tangKataNachyl); + } + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + // jezeli jest droga na kierunku to przejezdnosc 1 (zakładam, że dane są kompletne i istnienie drogi + // na tym odcinku implikuje istnienie drogi na komplementarnym odcinku kwadratu docelowego) tj. + // kwStop.jestDroga[nowyKierunek.oppositeDirectId] == true + if (kwStart.jestDroga[nowyKierunek.id]) { + return stopienPrzejezdNaDrodzeNachylenie(tangKataNachyl); + } + double stopPrzejezdNachylenie = stopienPrzejezdNaPrzelajNachylenie(tangKataNachyl); + if (stopPrzejezdNachylenie <= 0.0) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + float stop_pow_czysta_kw_start = 1.0f - kwStart.stopienZalesienia + - kwStart.stopienZabagnienia - kwStart.stopienZawodnienia - kwStart.stopienZabudowy; + float stop_pow_czysta_kw_stop = 1.0f - kwStop.stopienZalesienia + - kwStop.stopienZabagnienia - kwStop.stopienZawodnienia - kwStop.stopienZabudowy; + double stopienPrzejezdKwadratStart = stop_pow_czysta_kw_start + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id]; + if (stop_pow_czysta_kw_start < 0.5f) { + stopienPrzejezdKwadratStart += kwStart.stopienZalesienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZabudowy + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZawodnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZabagnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id]; + } + double stopienPrzejezdKwadratStop = stop_pow_czysta_kw_stop + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id]; + if (stop_pow_czysta_kw_stop < 0.5f) { + stopienPrzejezdKwadratStop += kwStop.stopienZalesienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZabudowy + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZawodnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZabagnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id]; + } + if (stopienPrzejezdKwadratStart < 0.05 || stopienPrzejezdKwadratStop < 0.05) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + double wynik = (stopienPrzejezdKwadratStart + stopienPrzejezdKwadratStop) * 0.5f; + wynik *= stopPrzejezdNachylenie; + return (przejezdnoscZawsze) ? Math.max(minStopienPrzejezd, wynik) : wynik; + } + + /** + * Funkcja wyznacza stopień przejezdności między dwoma sąsiednimi kwadratami + * uwzględniając warunki terenowe oraz możliwości sprzętu w zakresie pokonywania terenu, w tym rzek i rowów. + * + * @param xStart współrzędna GridCoord.X kwadratu startowego + * @param yStart współrzędna GridCoord.Y kwadratu startowego + * @param xStop współrzędna GridCoord.X kwadratu docelowego + * @param yStop współrzędna GridCoord.Y kwadratu docelowego + * @param zKierunku parametr informujący, z którego kierunku znaleziono się w kwadracie startowym + * @param podwozie rodzaj podwozia + * @param szerokoscPokonywRowow szerokość pokonywanych rowów [m] + * @param glebokoscBrodzenia maksymalna głębokość brodzenia [m] + * @param predkoscPlywania maksymalna predkość pływania [m] (value == 0, to brak możliwości pływania) + * @return stopień przejezdności pomiędzy dwoma sąsiednimi kwadratami (wartości z przedziału [0.0, 1.0]) + */ + public static double getStopienPrzejezdnosciNew(int xStart, int yStart, int xStop, int yStop, EGeoDirection zKierunku, + ERodzajPodwozia podwozie, + double szerokoscPokonywRowow, + double glebokoscBrodzenia, + double predkoscPlywania) { + if (podwozie == ERodzajPodwozia.NIEOKRESLONE) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + if (xStart == xStop && yStart == yStop) { + return getStopienPrzejezdnosci(xStart, yStart, podwozie); + } + Kwadrat kwStart = Teren.getKwadrat(xStart, yStart); + Kwadrat kwStop = Teren.getKwadrat(xStop, yStop); + if (kwStart == EMPTY_SQUARE || kwStop == EMPTY_SQUARE) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + EGeoDirection nowyKierunek = GeomUtils.kierunekDlaSasiada(xStart, yStart, xStop, yStop); + if (EGeoDirection.UNDEFINED == nowyKierunek) { + LOGGER.warn("Błędna wartość kierunku przy wyznaczaniu stopnia przejezdności."); + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + int roznicaWysokosci = kwStop.wysokoscSrednia - kwStart.wysokoscSrednia; + double odleglosc = GeomUtils.odlegloscKwadratowSasiednich(nowyKierunek); + double tangKataNachyl = roznicaWysokosci / odleglosc; + double stopienPrzejezdKwadratStart = 1.0; + double stopienPrzejezdKwadratStop = 1.0; + if (podwozie != ERodzajPodwozia.PODUSZKA_POW) { + if (kwStart.stopienZawodnienia > 0.0f) { + if (predkoscPlywania > 0.0) { + stopienPrzejezdKwadratStart = 0.1; + } else { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + } + if (kwStop.stopienZawodnienia > 0.0f) { + if (predkoscPlywania > 0.0) { + stopienPrzejezdKwadratStop = 0.1; + } else { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + } + if (stopienPrzejezdKwadratStart + stopienPrzejezdKwadratStop >= 2.0) { + // uwzględnienie przeszkodów wodnych tylko gdy badane kwadraty bez pokrycia wodami powierzchniowymi + boolean jestPrzeszkoda = Teren.jestPrzeszkodaNaKierunkuRuchu(xStart, yStart, zKierunku, nowyKierunek); + if (jestPrzeszkoda) { + if (kwStart.jestDroga[zKierunku.oppositeDirectId] && kwStart.jestDroga[nowyKierunek.id]) { + // sprawdzam, czy istnieje droga z kierunku wejsciowego (kierunek przeciwny do "zKierunku") + // do wyjściowego "nowyKierunek", zatem istnieje most + return stopienPrzejezdNaDrodzeNachylenie(tangKataNachyl); + } else { + // jeśli jest przeszkoda wodna i nie ma mostu, to sprawdzam możliwości przeprawowe sprzętu + //TODO wyznaczyć przejezdność na podstawie możliwości przeprawowych sprzętu + if (predkoscPlywania > 0.0) { + stopienPrzejezdKwadratStart = 0.1; + } else { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + } + } + } + } + // jezeli jest droga na kierunku to przejezdnosc 1 (zakładam, że dane są kompletne i istnienie drogi + // na tym odcinku implikuje istnienie drogi na komplementarnym odcinku kwadratu docelowego) tj. + // kwStop.jestDroga[nowyKierunek.oppositeDirectId] == true + if (kwStart.jestDroga[nowyKierunek.id]) { + return stopienPrzejezdNaDrodzeNachylenie(tangKataNachyl); + } + double stopPrzejezdNachylenie = stopienPrzejezdNaPrzelajNachylenie(tangKataNachyl); + if (stopPrzejezdNachylenie <= 0.0) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + if (stopienPrzejezdKwadratStart >= 1.0) { + float stop_pow_czysta_kw_start = 1.0f - kwStart.stopienZalesienia + - kwStart.stopienZabagnienia - kwStart.stopienZawodnienia - kwStart.stopienZabudowy; + stopienPrzejezdKwadratStart = stop_pow_czysta_kw_start + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id]; + if (stop_pow_czysta_kw_start < 0.5f) { + stopienPrzejezdKwadratStart += kwStart.stopienZalesienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZabudowy + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZawodnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id]; + stopienPrzejezdKwadratStart += kwStart.stopienZabagnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id]; + } + } + if (stopienPrzejezdKwadratStop >= 1.0) { + float stop_pow_czysta_kw_stop = 1.0f - kwStop.stopienZalesienia + - kwStop.stopienZabagnienia - kwStop.stopienZawodnienia - kwStop.stopienZabudowy; + stopienPrzejezdKwadratStop = stop_pow_czysta_kw_stop + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id]; + if (stop_pow_czysta_kw_stop < 0.5f) { + stopienPrzejezdKwadratStop += kwStop.stopienZalesienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZabudowy + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZawodnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id]; + stopienPrzejezdKwadratStop += kwStop.stopienZabagnienia + * Teren.STOPIEN_PRZEJEZDNOSCI[podwozie.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id]; + } + } + if (stopienPrzejezdKwadratStart < 0.05 || stopienPrzejezdKwadratStop < 0.05) { + return (przejezdnoscZawsze) ? minStopienPrzejezd : 0.0f; + } + double wynik = (stopienPrzejezdKwadratStart + stopienPrzejezdKwadratStop) * 0.5f; + wynik *= stopPrzejezdNachylenie; + return (przejezdnoscZawsze) ? Math.max(minStopienPrzejezd, wynik) : wynik; + } + + /** + * Funkcja sprawdza, czy w danym kwadracie na danym, złożonym kierunku ruchu + * zadanym przez staryKier-nowyKierunek znajduje się przeszkoda. + * + * @param xStart współrzędna X kwadratu startowego przemieszczenia + * @param yStart współrzędna Y kwadratu startowego przemieszczenia + * @param staryKier stary kierunek ruchu + * @param nowyKierunek nowy kierunek ruchu + * @return true, jeśli jest przeszkoda w danym kwadracie na danym kierunku ruchu zadanym przez staryKier-nowyKierunek + */ + public static boolean jestPrzeszkodaNaKierunkuRuchu(int xStart, int yStart, EGeoDirection staryKier, EGeoDirection nowyKierunek) { + // Najpierw sprawdzenie, czy przechodząc po przekątnej nie przecinam przeszkody wodnej lub rowu prostopadłej do ruchu + // przechodzącej przez kwadraty sąsiednie bokami np. dla NORTHEAST -> NORTH i EAST + Kwadrat kwSasiedni; + boolean jestPrzeszkoda = false; + switch (nowyKierunek) { + case NORTHEAST: + kwSasiedni = getKwadrat(xStart, yStart + 1); + jestPrzeszkoda = kwSasiedni.jestPrzeszkodaWodna[EGeoDirection.SOUTHEAST.id]; + break; + case SOUTHEAST: + kwSasiedni = getKwadrat(xStart, yStart - 1); + jestPrzeszkoda = kwSasiedni.jestPrzeszkodaWodna[EGeoDirection.NORTHEAST.id]; + break; + case SOUTHWEST: + kwSasiedni = getKwadrat(xStart, yStart - 1); + jestPrzeszkoda = kwSasiedni.jestPrzeszkodaWodna[EGeoDirection.NORTHWEST.id]; + break; + case NORTHWEST: + kwSasiedni = getKwadrat(xStart, yStart + 1); + jestPrzeszkoda = kwSasiedni.jestPrzeszkodaWodna[EGeoDirection.SOUTHWEST.id]; + break; + default: + } + if (jestPrzeszkoda) { + return true; + } + Kwadrat kwStart = getKwadrat(xStart, yStart); + boolean[] przeszkodaWodna = kwStart.jestPrzeszkodaWodna; + boolean[] row = ROWY_DLA_PRZEJEZDNOSCI; + if (nowyKierunek == EGeoDirection.UNDEFINED) { + // przeszkoda + return true; + } + // zmieniam zwrot poprzedniego wektora ruchu, aby oba wektory wychodziły z tego samego punktu + staryKier = staryKier.oppositeDirect(); + if (staryKier == nowyKierunek) { + // brak przeszkody na drodze powrotnej + return false; + } + if (przeszkodaWodna[nowyKierunek.id] || row[nowyKierunek.id]) { + // przeszkoda na kierunku ruchu + return true; + } + if (staryKier == EGeoDirection.UNDEFINED) { + // brak przeszkody + return false; + } + EGeoDirection kierSasiedni = staryKier; + // sprawdzam kierunki sąsiędnie idąc zgodnie z ruchem wskazówek zegara + do { + kierSasiedni = kierSasiedni.rightNextDirect(); + if (przeszkodaWodna[kierSasiedni.id] || row[kierSasiedni.id]) { + // wychodzę z petli, aby sprawdzić kierunki sąsiędnie idąc przeciwnie do ruchu wskazówek zegara + break; + } + } while (kierSasiedni != nowyKierunek); + if (kierSasiedni == nowyKierunek) { + // brak przeszkody na kierunku ruchu + return false; + } + // sprawdzam kierunki sąsiędnie idąc przeciwnie do ruchu wskazówek zegara + kierSasiedni = staryKier; + do { + kierSasiedni = kierSasiedni.leftNextDirect(); + if (przeszkodaWodna[kierSasiedni.id] || row[kierSasiedni.id]) { + // przeszkoda na kierunku ruchu + return true; + } + } while (kierSasiedni != nowyKierunek); + // brak przeszkody + return false; + } + + /** + * Funkcja sprawdza, czy kwadrat przejezdny + * + * @param gridCoord testowany kwadrat + * @return true, jeśli jest przejezdny + */ + public static boolean czyKwadratPrzyjezdny(GridCoord gridCoord) { + return getStopienPrzejezdnosci(gridCoord, ERodzajPodwozia.GASIENICE) > 0.01; + } + + private Teren() { + } + + static { + for (int i = 0; i < history.length; i++) { + history[i] = new GridCoord(); + } + przejezdnoscZawsze = MapConsts.ustawienia.getProperty("przejezdnosc_zawsze").equals("on"); + minStopienPrzejezd = Double.parseDouble(MapConsts.ustawienia.getProperty("minimalny_stopien_przejezdnosci")); + + minStopienPrzejezdNaPrzelaj = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.minimalny_na_przelaj")); + minStopienPrzejezdNaDrodzeNachylenie = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.minimalny.na_drodze.nachylenie_terenu")); + minStopienPrzejezdNaPrzelajNachylenie = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.minimalny.na_przelaj.nachylenie_terenu")); + + minKatNachylTerenuNaDrodze = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.na_drodze.nachylenie_terenu.kat_minimalny")); + minKatNachylTerenuNaDrodze = Math.max(0, minKatNachylTerenuNaDrodze); + minKatNachylTerenuNaDrodze = Math.min(60, minKatNachylTerenuNaDrodze); + minKatNachylTerenuNaDrodze *= Math.PI / 180; + + maxKatNachylTerenuNaDrodze = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.na_drodze.nachylenie_terenu.kat_maksymalny")); + maxKatNachylTerenuNaDrodze = Math.max(0, maxKatNachylTerenuNaDrodze); + maxKatNachylTerenuNaDrodze = Math.min(60, maxKatNachylTerenuNaDrodze); + maxKatNachylTerenuNaDrodze *= Math.PI / 180; + if (maxKatNachylTerenuNaDrodze < minKatNachylTerenuNaDrodze) { + double temp = maxKatNachylTerenuNaDrodze; + maxKatNachylTerenuNaDrodze = minKatNachylTerenuNaDrodze; + minKatNachylTerenuNaDrodze = temp; + } + + minKatNachylTerenuNaPrzelaj = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.na_przelaj.nachylenie_terenu.kat_minimalny")); + minKatNachylTerenuNaPrzelaj = Math.max(0, minKatNachylTerenuNaPrzelaj); + minKatNachylTerenuNaPrzelaj = Math.min(60, minKatNachylTerenuNaPrzelaj); + minKatNachylTerenuNaPrzelaj *= Math.PI / 180; + + maxKatNachylTerenuNaPrzelaj = Double.parseDouble(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.na_przelaj.nachylenie_terenu.kat_maksymalny")); + maxKatNachylTerenuNaPrzelaj = Math.max(0, maxKatNachylTerenuNaPrzelaj); + maxKatNachylTerenuNaPrzelaj = Math.min(60, maxKatNachylTerenuNaPrzelaj); + maxKatNachylTerenuNaPrzelaj *= Math.PI / 180; + if (maxKatNachylTerenuNaPrzelaj < minKatNachylTerenuNaPrzelaj) { + double temp = maxKatNachylTerenuNaPrzelaj; + maxKatNachylTerenuNaPrzelaj = minKatNachylTerenuNaPrzelaj; + minKatNachylTerenuNaPrzelaj = temp; + } + + TANG_ALFA_MAX_NA_DRODZE = Math.tan(maxKatNachylTerenuNaDrodze); + TANG_ALFA_MIN_NA_DRODZE = Math.tan(minKatNachylTerenuNaDrodze); + a1 = (minStopienPrzejezdNaDrodzeNachylenie - 1) / (TANG_ALFA_MAX_NA_DRODZE - TANG_ALFA_MIN_NA_DRODZE); + b1 = 1 - a1 * TANG_ALFA_MIN_NA_DRODZE; + + TANG_ALFA_MAX_NA_PRZELAJ = Math.tan(maxKatNachylTerenuNaPrzelaj); + TANG_ALFA_MIN_NA_PRZELAJ = Math.tan(minKatNachylTerenuNaPrzelaj); + a2 = (minStopienPrzejezdNaPrzelajNachylenie - 1) / (TANG_ALFA_MAX_NA_PRZELAJ - TANG_ALFA_MIN_NA_PRZELAJ); + b2 = 1 - a2 * TANG_ALFA_MIN_NA_PRZELAJ; + + STOPIEN_PRZEJEZDNOSCI = new float[ERodzajPodwozia.numberOfValues()][ERodzajTerenuPokrycie.numberOfValues()]; + + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_gasienicowe.teren_zabudowany")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_gasienicowe.teren_zalesiony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_gasienicowe.teren_zabagniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_gasienicowe.teren_zawodniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.GASIENICE.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_gasienicowe.teren_czysty")); + + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA_GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zabudowany")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA_GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zalesiony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA_GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zabagniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA_GASIENICE.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zawodniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA_GASIENICE.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_czysty")); + + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowe.teren_zabudowany")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowe.teren_zalesiony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowe.teren_zabagniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowe.teren_zawodniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.KOLA.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_kolowe.teren_czysty")); + + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PODUSZKA_POW.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_poduszka.teren_zabudowany")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PODUSZKA_POW.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_poduszka.teren_zalesiony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PODUSZKA_POW.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_poduszka.teren_zabagniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PODUSZKA_POW.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_poduszka.teren_zawodniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PODUSZKA_POW.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_poduszka.teren_czysty")); + + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PLOZY.id][ERodzajTerenuPokrycie.TEREN_ZABUDOWANY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_plozy.teren_zabudowany")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PLOZY.id][ERodzajTerenuPokrycie.TEREN_ZALESIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_plozy.teren_zalesiony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PLOZY.id][ERodzajTerenuPokrycie.TEREN_ZABAGNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_plozy.teren_zabagniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PLOZY.id][ERodzajTerenuPokrycie.TEREN_ZAWODNIONY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_plozy.teren_zawodniony")); + STOPIEN_PRZEJEZDNOSCI[ERodzajPodwozia.PLOZY.id][ERodzajTerenuPokrycie.TEREN_CZYSTY.id] = + Float.parseFloat(MapConsts.ustawienia.getProperty("stopien_przejezdnosci.podwozie_plozy.teren_czysty")); + for (int i = 0; i < BIG_X_MAX; i++) { + for (int j = 0; j < BIG_Y_MAX; j++) { + bsSynch[i][j] = new Object(); + } + } + } + + public static void main(String[] args) throws Exception { +// boolean[] przeszk = new boolean[8]; +// boolean[] row = new boolean[8]; +// przeszk[2] = true; +// przeszk[6] = true; + +// Teren.poprawDaneWysokosciowe(); + + LOGGER.debug("start"); +// Teren.normalizujDanePokrycia(); + + String dir = "C:/users/jrulka/Workspace/_data/new/"; +// Set fileNames = NMTDataProvider.listFiles("C:/Workspace/_data/swdt/ms4ds/teren/kwadraty/100m/"); +// Teren.wygenerujCzysteDane(100, false, false, false, false, true, false, false, false, false); + Teren.wygenerujNoweDane(dir, 20); +// Teren.wygenerujCzysteDane(dir, 25, false, false, false, false, true, false, false, false, false); +// Teren.wyzerujDane(); +// Teren.zapisBuforaMapyDoPliku(); + + // test zapisu i odczytu danych w formacie byte (dane są w kodzie uzupelnieniowym do dwóch tzn. -128..127) +// StringBuilder sb = new StringBuilder(100); +// sb.append(MapConsts.KWADRATY_DIR); +// sb.append("aaa.bin"); +// ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(sb.toString())); +// int hex = (int) (1.0f * 255.0f); +//// hex = (hex > 255) ? 255 : hex; +//// out.writeByte(hex); +// int hex1 = (int) (0.0f * 255.0f); +//// out.writeByte(hex1); +// int hex2 = (int) (0.99 * 255.0f); +// out.writeByte(0); +// out.writeByte(1); +// out.writeByte(20); +// out.writeByte(100); +// out.writeByte(5); +// out.writeByte(127); +// out.writeByte(128); +// out.close(); +// +// ObjectInputStream in = new ObjectInputStream(new FileInputStream(sb.toString())); +// hex = in.readByte(); +// hex1 = in.readByte(); +// hex2 = in.readByte(); +// hex2 = in.readByte(); +// hex2 = in.readByte(); +// hex2 = in.readByte(); +// byte b = in.readByte(); +// in.close(); + + +// WYZEROWANIE DROG W TERENIE +// for (int x = 14; x < 14 + 11; x++) { +// for (int dx = 0; dx < 4; dx++) { +// char cx = LITERALS.charAt(dx); +// for (int y = 139; y < 139 + 7; y++) { +// for (int dy = 0; dy < 6; dy++) { +// char cy = LITERALS.charAt(dy); +// String fn = String.format("%1$s%2$03d%3$c%4$03d%5$c", PATH, x, cx, y, cy); +// try { +// RightBigSquare bs = new RightBigSquare(fn); +// for (int i = 0; i < bs.kwadraty.length; i++) { +// for (int j = 0; j < bs.kwadraty[i].length; j++) { +// Kwadrat kw = bs.kwadraty[i][j]; +//// kw.jestDroga = new boolean[8]; +// kw.stopienZalesienia = 0; +// } +// } +// bs.liczbaZmian = 1; +// bs.updateFile(true); +// } catch (IOException e) { +//// e.printStackTrace(); +// } +// } +// } +// } +// } + +// try { +// RightBigSquare bs = new RightBigSquare("au2data/new_teren/Polska/kwadraty/50m/014A142D", true); +// +// bs.liczbaZmian = 1; +//// bs.updateFile(true, true); +// bs.writeFileBinary(true, 50); +// } catch (IOException e) { +//// e.printStackTrace(); +// } + + +// WYZEROWANIE WYBRANYCH DANYCH KWADRATOW I ZAPIS W PLIKU WYBRANYM FORMACIE I NAZWIE +// for (int x = 14; x < 14 + 11; x++) { +// for (int dx = 0; dx < 4; dx++) { +// char cx = LITERALS.charAt(dx); +// for (int y = 49; y < 49 + 7; y++) { +//// for (int y = 139; y < 139 + 7; y++) { +// for (int dy = 0; dy < 6; dy++) { +// char cy = LITERALS.charAt(dy); +// // NOWY FORMAT NAZWY PLIKU +//// String fn = String.format("%1$s%2$03d%3$s%4$02d%5$c", "E", x, cx + "_N", y, cy); +// // STARY FORMAT NAZWY PLIKU +// String fn = String.format("%1$03d%2$c%3$03d%4$c", x, cx, y + 90, cy); +// try { +// RightBigSquare.binary = false; +// RightBigSquare bs = new RightBigSquare(fn); +// // wyzerowanie tylko drog +// bs.resetSquares(false, false, false, false, true, false, false); +// bs.liczbaZmian = 1; +// // NOWY FORMAT NAZWY PLIKU +//// fn = String.format("%1$s%2$03d%3$s%4$02d%5$c", "E", x, cx + "_N", y, cy); +// RightBigSquare.binary = false; +// bs.updateFile(true, fn); +// } catch (IOException e) { +//// e.printStackTrace(); +// } +// } +// } +// } +// } + + } + + public static void wyzerujDane() { + // WYGENEROWANIE CZYSTYCH PLIKÓW DANYCH + for (int x = 0; x < bigSquares.length; x++) { + for (int y = 0; y < bigSquares[x].length; y++) { + try { + BigSquare bs = loadArea(x, y); + if (bs != null && bs instanceof RightBigSquare) { + RightBigSquare rbs = (RightBigSquare) bs; + // wyzerowanie wszystkiego poza wysokością i różnicą wzniesień + rbs.resetSquares(true, true, true, true, true, true, true, true, true); + rbs.liczbaZmian = 1; + rbs.updateFile(true, null); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + } + } + + /** + * Funkcja uzupełnia brakujące dane wysokosciowe jako średnia kwadratów sąsiednich o niezerowych wysokosciach. + * Funkcja powinna być wywoływana dla danych zawierających informacje o wodach morskich. + */ + public static void poprawDaneWysokosciowe() { + int maxX = MapConsts.SS_PER_BS_X * MapConsts.BS_PER_DEG_X * MapConsts.DX_REF; + int maxY = MapConsts.SS_PER_BS_Y * MapConsts.BS_PER_DEG_Y * MapConsts.DY_REF; + for (int x = 0; x < maxX; x++) { + for (int y = 0; y < maxY; y++) { + Kwadrat kw = getKwadrat(x, y); + if (kw == EMPTY_SQUARE) { + continue; + } + if (kw.stopienZawodnienia == 0 && kw.wysokoscSrednia == 0) { + // operacja dla kwadratów lądowych + int licz = 0; + int suma = 0; + for (int i = -1; i < 1; i++) { + for (int j = -1; j < 1; j++) { + if (i == 0 && j == 0) { + // pomijam dane bieżącego kwadratu + continue; + } + int xx = x + i; + int yy = y + j; + Kwadrat kwSasiad = getKwadrat(xx, yy); + if (kwSasiad == EMPTY_SQUARE) { + // pomijam kwadraty poza mapą + continue; + } + if (kwSasiad.wysokoscSrednia > 0) { + // uśredniam tylko po kwadratach o niezerowej wysokości + suma += kwSasiad.wysokoscSrednia; + licz++; + } + } + } + float wys = (float) suma / (float) licz; + kw.wysokoscSrednia = (int) wys; + } + } + } + for (int x = 0; x < bigSquares.length; x++) { + for (int y = 0; y < bigSquares[x].length; y++) { + try { + BigSquare bs = bigSquares[x][y]; + if (bs != null && bs instanceof RightBigSquare) { + RightBigSquare rbs = (RightBigSquare) bs; + rbs.liczbaZmian = 1; + rbs.updateFile(true, null); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + } + } + + /** + * Funkcja poprawia dane o pokryciu, aby dane sumowały się do jedynki. + */ + static void normalizujDanePokrycia() { + int maxX = MapConsts.SS_PER_BS_X * MapConsts.BS_PER_DEG_X * MapConsts.DX_REF; + int maxY = MapConsts.SS_PER_BS_Y * MapConsts.BS_PER_DEG_Y * MapConsts.DY_REF; + for (int x = 0; x < maxX; x++) { + for (int y = 0; y < maxY; y++) { + Kwadrat kw = getKwadrat(x, y); + if (kw == EMPTY_SQUARE) { + continue; + } + float suma = kw.stopienZalesienia + kw.stopienZawodnienia + kw.stopienZabudowy + kw.stopienZabagnienia; + if (suma > 1.0f) { + kw.stopienZalesienia /= suma; + kw.stopienZawodnienia /= suma; + kw.stopienZabudowy /= suma; + kw.stopienZabagnienia = 1.0f - kw.stopienZalesienia - kw.stopienZawodnienia - kw.stopienZabudowy; + } + } + } + for (int x = 0; x < bigSquares.length; x++) { + for (int y = 0; y < bigSquares[x].length; y++) { + try { + BigSquare bs = bigSquares[x][y]; + if (bs != null && bs instanceof RightBigSquare) { + RightBigSquare rbs = (RightBigSquare) bs; + rbs.liczbaZmian = 1; + rbs.updateFile(true, null); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + } + } + + /** + * @param dir katalog z danymi terenowymi np. "d:/Workspace2/kwadraty/czyste-wysokosc/" + * @param dlmk docelowy rozmiar generowanych kwadratów terenu + */ + public static void wygenerujNoweDane(String dir, int dlmk) { + // WYGENEROWANIE CZYSTYCH PLIKÓW DANYCH Z ZACHOWANIEM TYLKO WYSOKOSCI I ROZNICY POZIOMOW WZNIESIEN + for (int x = 0; x < bigSquares.length; x++) { + for (int y = 0; y < bigSquares[x].length; y++) { + try { + BigSquare bs = loadArea(x, y); + if (bs instanceof RightBigSquare ) { + RightBigSquare rbs = (RightBigSquare) bs; + // wyzerowanie wszystkiego poza wysokością i różnicą wzniesień +// rbs.resetSquares(true, true, true, true, true, true, true, true, true); + rbs.saveNewFileWithNewScale(dir, dlmk); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/TerrainType.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/TerrainType.java new file mode 100644 index 0000000..decb47b --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/TerrainType.java @@ -0,0 +1,38 @@ +package pl.wat.ms4ds.terenfunkcje; + +public enum TerrainType { + BARE_GROUND(0), + GRASS(1), + SWAMP(2), + WATER(3), + SCRUB_BUSHES(4), + BUILDINGS(5), + FOREST(6); + + static { + BARE_GROUND.height = 0; + BARE_GROUND.passability = true; + + } + + public int getHeight(byte terrainType) { + return height; + } + + public final int ID; + private int height; + /** + * Zdolność przekraczania + */ + private boolean passability; + + TerrainType(int 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/terenfunkcje/TerrainUtils.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/TerrainUtils.java new file mode 100644 index 0000000..3f73c6b --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/TerrainUtils.java @@ -0,0 +1,166 @@ +package pl.wat.ms4ds.terenfunkcje; + +import pl.wat.ms4ds.common.EGeoDirection; +import pl.wat.ms4ds.common.ERodzajPodwozia; + +import java.util.ArrayList; + + +public class TerrainUtils { + + // ======================================================================== + + public static float widocznoscOptyczna(float wysokoscObserwatora, float wysokoscCelu, + int x1, int y1, int x2, int y2) { + if ((x1 == x2) && (y1 == y2)) { + return 1.0f; + } + Kwadrat kwDo = Teren.getKwadrat(x1, y1); + Kwadrat kwOd = Teren.getKwadrat(x2, y2); + if (kwDo == Kwadrat.EMPTY_SQUARE || kwOd == Kwadrat.EMPTY_SQUARE) { + return 0.0f; + } + // roznica wysokosci miedzy skrajnymi kwadratami + float roznicaWysokosci = kwDo.wysokoscSrednia + wysokoscCelu - kwOd.wysokoscSrednia - wysokoscObserwatora; + float wysBezwzgObserwatora; + if (roznicaWysokosci < 0) { + // sprawdzanie kwOd -> kwDo + int swap = x1; + x1 = x2; + x2 = swap; + swap = y1; + y1 = y2; + y2 = swap; + roznicaWysokosci = -roznicaWysokosci; + wysBezwzgObserwatora = kwDo.wysokoscSrednia + wysokoscCelu; + } else { + wysBezwzgObserwatora = kwOd.wysokoscSrednia + wysokoscObserwatora; + } + GridCoord[] kwadratyNaOdcinku = Bresenham.generateSegment(x1, y1, x2, y2); + float dlugoscOdcinka = GridCoord.odleglosc(x1, y1, x2, y2); + float tangAlfa0 = roznicaWysokosci / dlugoscOdcinka; + + float dh_max = 0; + for (int i = 1; i < kwadratyNaOdcinku.length - 1; i++) { + // badanie wewnetrznych kwadratow nalezacych do odcinka, + // czy nie sa powyzej linii widocznosci dla kwadratow skrajnych + float wysokoscPrzeszkody = 0.0f; + Kwadrat kwAkt = Teren.getKwadrat(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y); + if (kwAkt.stopienZalesienia > 0.5f) { + wysokoscPrzeszkody = 10.0f; + } + if (kwAkt.stopienZabudowy > 0.5f) { + wysokoscPrzeszkody = 10.0f; + } + // wyznaczenie roznicy wysokosci kwadratu badanego i docelowego + // uwzgledniajac wysokosc obserwatora oraz wysokosc przeszkody + float roznWysAkt = kwAkt.wysokoscSrednia + wysokoscPrzeszkody - wysBezwzgObserwatora; + if (dh_max >= roznWysAkt) { + continue; + } + + float odleg = GridCoord.odleglosc(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y, x1, y1); +// float tangAlfa = roznWysAkt / odleg; +// if (tangAlfa0 < tangAlfa) { + if (tangAlfa0 * odleg < roznWysAkt) { + // wysokosc aktualnie badanego kwadratu jest powyzej/ponizej + // linii poprowadzonej z kwadratu startowego do docelowego (z uwzglednieniem wysokosci obserwatora i celu) + // odpowiednio dla katow dodatnich/ujemnych + + return 0.0f; + } + dh_max = roznWysAkt; + } + return 1.0f; + } + + public static float widocznoscOptyczna(int x, int y) { + Kwadrat kw = Teren.getKwadrat(x, y); + if (kw.stopienZabudowy > 0.25f || kw.stopienZalesienia > 0.25f) { + return 0.3f; + } + return 1.0f; + } + + public static float widocznoscOptyczna2(float wysokoscObserwatora, float wysokoscCelu, + int x1, int y1, int x2, int y2) { + if ((x1 == x2) && (y1 == y2)) { + return 1.0f; + } + Kwadrat kwDo = Teren.getKwadrat(x1, y1); + Kwadrat kwOd = Teren.getKwadrat(x2, y2); + if (kwDo == Kwadrat.EMPTY_SQUARE || kwOd == Kwadrat.EMPTY_SQUARE) { + return 0.0f; + } + // roznica wysokosci miedzy skrajnymi kwadratami + float roznicaWysokosci = kwDo.wysokoscSrednia + wysokoscCelu - kwOd.wysokoscSrednia - wysokoscObserwatora; + GridCoord[] kwadratyNaOdcinku = GeomUtils.kwadratyOdcinka(x1, y1, x2, y2); + float dlugoscOdcinka = GridCoord.odleglosc(x1, y1, x2, y2); + float tangAlfa0 = roznicaWysokosci / dlugoscOdcinka; + for (int i = 1; i < kwadratyNaOdcinku.length - 1; i++) { + // badanie wewnetrznych kwadratow nalezacych do odcinka, + // czy nie sa powyzej linii widocznosci dla kwadratow skrajnych + float wysokoscPrzeszkody = 0.0f; + Kwadrat kwAkt = Teren.getKwadrat(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y); + if (kwAkt.stopienZalesienia > 0.5f) { + wysokoscPrzeszkody = 10.0f; + } + if (kwAkt.stopienZabudowy > 0.5f) { + wysokoscPrzeszkody = 10.0f; + } + // wyznaczenie roznicy wysokosci kwadratu badanego i docelowego + // uwzgledniajac wysokosc obserwatora oraz wysokosc przeszkody + float roznWysAkt = kwAkt.wysokoscSrednia + wysokoscPrzeszkody - kwOd.wysokoscSrednia - wysokoscObserwatora; + float odleg = GridCoord.odleglosc(kwadratyNaOdcinku[i].x, kwadratyNaOdcinku[i].y, x1, y1); + float tangAlfa = roznWysAkt / odleg; + if (tangAlfa0 < tangAlfa) { + // wysokosc aktualnie badanego kwadratu jest powyzej/ponizej + // linii poprowadzonej z kwadratu startowego do docelowego (z uwzglednieniem wysokosci obserwatora i celu) + // odpowiednio dla katow dodatnich/ujemnych + + return 0.0f; + } + } + if (kwDo.stopienZabudowy > 0.25f || kwDo.stopienZalesienia > 0.25f) { + return 0.3f; + } + return 1.0f; + } + + public static float sredStopienWidoczOptycznej(float wysokoscObserwatora, float wysokoscCelu, + GridCoord kwadratOd, int dl1, GridCoord 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; + } + + public static float sredStopienWidoczOptycznej2(float wysokoscObserwatora, float wysokoscCelu, + GridCoord kwadratOd, int dl1, GridCoord 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 += widocznoscOptyczna2(wysokoscObserwatora, wysokoscCelu, x1, y1, x2, y2); + + stop /= (dl1 * dl1 * dl2 * dl2); + return stop; + } + + public static float stopienPrzejezdnosciAktualOdcinkaDrogi(ArrayList droga, int pozycja, + EGeoDirection zkierunku, ERodzajPodwozia podwozie) { + if ((droga == null) || (droga.size() == 0) || (pozycja < 0) || (pozycja >= droga.size() - 1)) { + return 0.0f; + } + GridCoord idKw1 = droga.get(pozycja); + GridCoord idKw2 = droga.get(pozycja + 1); + return (float) Teren.getStopienPrzejezdnosci(idKw1.x, idKw1.y, idKw2.x, idKw2.y, zkierunku, podwozie); + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/WezelDrogowy.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/WezelDrogowy.java new file mode 100644 index 0000000..d9e165e --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/WezelDrogowy.java @@ -0,0 +1,96 @@ +package pl.wat.ms4ds.terenfunkcje; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class WezelDrogowy { + int id = -1; + private int xms = 0; + private int yms = 0; + boolean jestMostem = false; + GridCoord idKw; + ArrayList luki = new ArrayList(); + + WezelDrogowy() { + } + + WezelDrogowy(String opis) { + if (null == opis || opis.length() == 0){ + SiecDrogowa.logger.debug("Pusty ciag opis w konstruktorze Wezla Drogowego."); + return; + } + StringTokenizer st = new StringTokenizer(opis, " \t"); + String[] tokenTable = new String[st.countTokens()]; + for (int i = 0; st.hasMoreTokens(); i++) { + tokenTable[i] = st.nextToken(); + } + if (tokenTable.length == 3) { + try { + this.xms = Integer.parseInt(tokenTable[0]); + //TODO KONSULTACJA WYWALAMY/ ODKOMENTOWUJEMY +// //Zabezpieczenie przed zaokraglaniem na krawedziach mapy +// if (this.xms < MapConsts.X_REF_MS) +// this.xms = MapConsts.X_REF_MS; +// if (this.xms > MapConsts.X_REF_MS + MapConsts.DX_REF_MS) +// this.xms = MapConsts.X_REF_MS + MapConsts.DX_REF_MS; + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z wezlami [Wezel: " + this.id + " X]."); + } + try { + this.yms = Integer.parseInt(tokenTable[1]); +// //Zabezpieczenie przed zaokraglaniem na krawedziach mapy +// if (this.yms < MapConsts.Y_REF_MS) +// this.yms = MapConsts.Y_REF_MS; +// if (this.yms > MapConsts.Y_REF_MS + MapConsts.DY_REF_MS) +// this.yms = MapConsts.Y_REF_MS + MapConsts.DY_REF_MS; + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z wezlami [Wezel: " + this.id + " Y]."); + } + try { + this.jestMostem = Integer.parseInt(tokenTable[2]) != 0 ? true : false; + } catch (NumberFormatException e) { + SiecDrogowa.logger.warn("Bledne dane w pliku z wezlami [Wezel: " + this.id + " czyJestMost]."); + } + int x = GridCoord.zamienWspXmsNaIdKwadratuX(getXms()); + int y = GridCoord.zamienWspYmsNaIdKwadratuY(getYms()); + this.idKw = new GridCoord(x, y); + } + else + { + SiecDrogowa.logger.warn("Bledne ilosc tokenow w linii [" + opis + "]."); + } + } + + public boolean isJestMostem() { + return jestMostem; + } + + public void setJestMostem(boolean jestMostem) { + this.jestMostem = jestMostem; + } + + public GridCoord getIdKw() { + return idKw; + } + + public void setIdKw(GridCoord idKw) { + this.idKw = new GridCoord(idKw); + } + + public void setXms(int xms) { + this.xms = xms; + } + + public int getXms() { + return xms; + } + + public void setYms(int yms) { + this.yms = yms; + } + + public int getYms() { + return yms; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/CoordUtils.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/CoordUtils.java new file mode 100644 index 0000000..853e895 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/CoordUtils.java @@ -0,0 +1,485 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.terenfunkcje.GeoCoord; +import pl.wat.ms4ds.terenfunkcje.GridCoord; +import pl.wat.ms4ds.terenfunkcje.Kwadrat; +import pl.wat.ms4ds.terenfunkcje.Teren; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.StringTokenizer; + +/** + * Odczyt danych wysokościowych z numerycznego modelu terenu (NMT_100). + *

+ * Kod źródłowy funkcji do transformacji współrzędnych elipsoidalnych na płaskie odwzorowań kartograficznych UTM, 1992, 2000 + */ +public class CoordUtils { + + private static final Logger logger = Logger.getLogger(CoordUtils.class); + + static String dataDir = "d:/Workspace2/dane_wysok/"; + + public static void main(String[] args) throws Exception { + + HashMap daneWysokHashMap = new HashMap(); + if (args.length > 0) { + dataDir = args[0]; + } + for (int i = 1; i < args.length; i++) { + String nmt_fn = args[i]; + daneWysokHashMap.clear(); + readData(nmt_fn, daneWysokHashMap); + for (DaneWysok daneWysok : daneWysokHashMap.values()) { + Kwadrat kw = Teren.getKwadrat(daneWysok.idKw.x, daneWysok.idKw.y); + kw.setWysokoscSrednia((int) (daneWysok.suma / daneWysok.licz + 0.5)); + } + logger.debug("Poczatek zapisu danych dla regionu " + nmt_fn + " >> " + i + "/" + (args.length - 1)); + Teren.zapisBuforaMapyDoPliku(); + logger.debug("Koniec zapisu danych dla regionu " + nmt_fn + " >> " + i + "/" + (args.length - 1)); + Teren.reset(); + } + logger.debug("Start: poprawy danych wysokosciowych"); + Teren.poprawDaneWysokosciowe(); + logger.debug("Koniec: poprawy danych wysokosciowych"); + + +// GeoCoord latLon = new GeoCoord(); +// PUWGCoord puwgCoord = new PUWGCoord(); +// puwgCoord.easting = 542800.0; +// puwgCoord.northing = 732200.0; +// puwgCoord.proj = 1; +// +// convertPuwgToLatLon(puwgCoord, latLon); +// logger.debug("latLon= (" + latLon.lat + ", " + latLon.lon + ")"); +// puwgCoord.easting = 718500.0; +// puwgCoord.northing = 663500.0; +// convertPuwgToLatLon(puwgCoord, latLon); +// logger.debug("latLon= (" + latLon.lat + ", " + latLon.lon + ")"); + + } + + private static void readData(String fileName, HashMap daneWysokHashMap) throws IOException { + try { + StringBuilder sb = new StringBuilder(100); + sb.append(dataDir); + sb.append(fileName); + sb.append(".txt"); + FileReader fis = new FileReader(sb.toString()); + // PUWG 1992 + PUWGCoord puwgCoord = new PUWGCoord(); + GeoCoord latLon = new GeoCoord(); + double wysokosc = 0.0; + StringTokenizer st = null; + String line = null; + BufferedReader br = new BufferedReader(fis); + if (br.ready()) { + line = br.readLine(); + int m = 1; + while (line != null) { + st = new StringTokenizer(line, " "); + if (st.countTokens() != 3) { + continue; + } + String[] tokTable = new String[st.countTokens()]; + for (int i = 0; st.hasMoreTokens(); i++) { + tokTable[i] = st.nextToken(); + } + try { + puwgCoord.easting = Double.parseDouble(tokTable[0]); + } catch (NumberFormatException e) { + logger.warn("Bledne dane w pliku: " + fileName); + } + try { + puwgCoord.northing = Double.parseDouble(tokTable[1]); + } catch (NumberFormatException e) { + logger.warn("Bledne dane w pliku: " + fileName); + } + try { + wysokosc = Double.parseDouble(tokTable[2]); + } catch (NumberFormatException e) { + logger.warn("Bledne dane w pliku: " + fileName); + } + convertPuwgToLatLon(puwgCoord, latLon); + GridCoord idKw = new GridCoord(latLon.lon, latLon.lat); + DaneWysok daneWysok = daneWysokHashMap.get(idKw); + if (daneWysok == null) { + daneWysok = new DaneWysok(idKw, wysokosc, 1); + daneWysokHashMap.put(idKw, daneWysok); + } else { + daneWysok.suma += wysokosc; + daneWysok.licz++; + } + line = br.readLine(); + if (m++ % 100000 == 0) { + System.out.print('-'); + } + } + } + br.close(); + System.out.println(); + logger.debug("Koniec odczytu pliku: " + fileName); + } catch (IOException e) { + throw e; + } + } + + private static final double fe = 500000.0; + + //Deklaracja tablicy stref rownoleżnikowych + private static final char[] cArray = {'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X'}; +// private static final String LITERALS = "CDEFGHJKLMNPQRSTUVWX"; + +//------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////// +//Funkcje pomocnicze + + /// ///////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------ + static double calculateESquared(double a, double b) { + a *= a; + b *= b; + return (a - b) / a; +// return ((a * a) - (b * b)) / (a * a); + } + + static double calculateE2Squared(double a, double b) { + a *= a; + b *= b; + return (a - b) / b; +// return ((a * a) - (b * b)) / (b * b); + } + + static double denom(double es, double sphi) { + double sinSphi = Math.sin(sphi); + return Math.sqrt(1.0 - es * (sinSphi * sinSphi)); + } + + static double sphsr(double a, double es, double sphi) { + double dn = denom(es, sphi); + return a * (1.0 - es) / (dn * dn * dn); + } + + static double sphsn(double a, double es, double sphi) { + double sinSphi = Math.sin(sphi); + return a / Math.sqrt(1.0 - es * (sinSphi * sinSphi)); + } + + static double sphtmd(double ap, double bp, double cp, double dp, double ep, 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)); + } + + //======================================================================= +// Funkcja służy do konwersji współrzednych elipsoidalnych B, L (lat/lon) WGS84 na płaskie X-northing, Y-easting odwzorowania kartograficznego UTM +//======================================================================= +// Argumenty wejściowe i wyjściowe: +// -------------------------------- +// int& utmXZone: nr strefy UTM wg. podziału południkowego (zwracane numery od 1 do 60, każda strefa ma sześć stopni) +// char& utmYZone: nr strefy wg. podziału równoleżnikowego (zwracane wartości: CDEFGHJKLMNPQRSTUVWX) +// double& easting: współrzędna Y UTM, w metrach po konwersji [metry] +// double& northing: współrzędna X UTM, w metrach po konwersji [metry] +// double lat, double lon: współrzędne lat/lon do konwersji [stopnie] +//======================================================================= + + /** + * + * @param lat współrzędna lat (szerokość geograficzna) do konwersji [stopnie] (układ WGS84) + * @param lon współrzędna lon (długość geograficzna) do konwersji [stopnie] (układ WGS84) + * @param utmCoord współrzędne UTM + */ + public static void convertLatLonToUtm(double lat, double lon, UTMCoord utmCoord) { + // Współczynnik zniekształcenia skali w południku osiowym + double tmd; + double nfn; + if (lon <= 0.0) { + utmCoord.xZone = 30 + (int) (lon / 6.0); + } else { + utmCoord.xZone = 31 + (int) (lon / 6.0); + } + if (lat < 84.0 && lat >= 72.0) { + // Specjalne zatrzymanie: strefa X ma 12 stopni od północy do południa, nie 8 + utmCoord.yZone = cArray[19]; + } else { + utmCoord.yZone = cArray[(int) ((lat + 80.0) / 8.0)]; + } + if (lat >= 84.0 || lat < -80.0) { + // Błędna wartość szerokości geograficznej (zwracany znak gwiazdki) + utmCoord.yZone = '*'; + } + double latRad = lat * DEG_2_RAD; + double lonRad = lon * DEG_2_RAD; + double olam = (utmCoord.xZone * 6 - 183) * DEG_2_RAD; + double dlam = lonRad - olam; + double s = Math.sin(latRad); + double c = Math.cos(latRad); + double t = s / c; + double eta = e2Squared * (c * c); + double sn = sphsn(a, eSquared, latRad); + tmd = sphtmd(ap, bp, cp, dp, ep, latRad); + double t1, t2, t3, t4, t5, t6, t7, t8, t9; + t1 = tmd * ok; + t2 = sn * s * c * ok / 2.0; + t3 = sn * s * (c * c * c) * ok * (5.0 - (t * t) + 9.0 * eta + 4.0 * (eta * eta)) / 24.0; + t4 = sn * s * (c * c * c * c * c) * ok * (61.0 - 58.0 * (t * t) + (t * t * t * t) + 270.0 * eta - 330.0 * (t * t) * eta + 445.0 * (eta * eta) + 324.0 * (eta * eta * eta) - 680.0 * (t * t) * (eta * eta) + 88.0 * (eta * eta * eta * eta) - 600.0 * (t * t) * (eta * eta * eta) - 192.0 * (t * t) * (eta * eta * eta * eta)) / 720.0; + t5 = sn * s * (c * c * c * c * c * c * c) * ok * (1385.0 - 3111.0 * (t * t) + 543.0 * (t * t * t * t) - (t * t * t * t * t * t)) / 40320.0; + if (latRad < 0.0) nfn = 10000000.0; + else nfn = 0; + utmCoord.northing = nfn + t1 + (dlam * dlam) * t2 + (dlam * dlam * dlam * dlam) * t3 + (dlam * dlam * dlam * dlam * dlam * dlam) * t4 + (dlam * dlam * dlam * dlam * dlam * dlam * dlam * dlam) * t5; + t6 = sn * c * ok; + t7 = sn * (c * c * c) * ok * (1.0 - (t * t) + eta) / 6.0; + t8 = sn * (c * c * c * c * c) * ok * (5.0 - 18.0 * (t * t) + (t * t * t * t) + 14.0 * eta - 58.0 * (t * t) * eta + 13.0 * (eta * eta) + 4.0 * (eta * eta * eta) - 64.0 * (t * t) * (eta * eta) - 24.0 * (t * t) * (eta * eta * eta)) / 120.0; + t9 = sn * (c * c * c * c * c * c * c) * ok * (61.0 - 479.0 * (t * t) + 179.0 * (t * t * t * t) - (t * t * t * t * t * t)) / 5040.0; + utmCoord.easting = fe + dlam * t6 + (dlam * dlam * dlam) * t7 + (dlam * dlam * dlam * dlam * dlam) * t8 + (dlam * dlam * dlam * dlam * dlam * dlam * dlam) * t9; + if (utmCoord.northing >= 9999999.0) utmCoord.northing = 9999999.0; + } + + //======================================================================= +// Funkcja służy do konwersji współrzednych elipsoidalnych WGS84 B, L (lat/lon) na płaskie X-northing, Y-easting odwzorowania kartograficznego 1992 i 2000 +//======================================================================= +// -------------------------------- +// int& utmXZone: nr strefy UTM wg. podziału południkowego (zwracane numery od 1 do 60, każda strefa ma sześć stopni) +// char& utmYZone: nr strefy wg. podziału równoleżnikowego (zwracane wartości: CDEFGHJKLMNPQRSTUVWX) +// double& easting: współrzędna Y UTM, w metrach po konwersji [metry] +// double& northing: współrzędna X UTM, w metrach po konwersji [metry] +// double lat, double lon: współrzędne lat/lon do konwersji [stopnie] +// int proj: odwzorowanie kartograficzne (proj = 1 odpowiada odwzorowaniu 1992, natomiast każda inna odwzorowaniu 2000) +//======================================================================= + public static void convertLatLonToPUWG(PUWGCoord puwgCoord, double lat, double lon) { + // Współczynnik zniekształcenia skli mapy w południku osiowym dla odwzorowania kartograficznego 2000 + //Współczynnik zniekształcenia skli mapy w południku osiowym dla odwzorowania kartograficznego 1992 + double ok_new = (puwgCoord.proj != 1) ? 0.999923 : 0.9993; + double olam = 0.0; + double tmd; + double nfn; + double strf = 0.0; + if (lon < 13.5 || lon > 25.5) { + //Błędna wartość długości geograficznej (zwracana wartość 99999999999999) + puwgCoord.easting = 999999999999999.0; + puwgCoord.northing = 999999999999999.0; + return; + } else { + if (puwgCoord.proj == 1) { + olam = 19.0 * DEG_2_RAD; + strf = 0.0; + nfn = -5300000.0; + } else { + nfn = 0; + if (lon >= 13.5 && lon < 16.5) { + olam = 15.0 * DEG_2_RAD; + strf = 5000000.0; + } + + if (lon >= 16.5 && lon < 19.5) { + olam = 18.0 * DEG_2_RAD; + strf = 6000000.0; + } + + if (lon >= 19.5 && lon < 22.5) { + olam = 21.0 * DEG_2_RAD; + strf = 7000000.0; + } + + if (lon >= 22.5 && lon < 25.5) { + olam = 24.0 * DEG_2_RAD; + strf = 8000000.0; + } + } + } + double latRad = lat * DEG_2_RAD; + double lonRad = lon * DEG_2_RAD; + double dlam = lonRad - olam; + double s = Math.sin(latRad); + double c = Math.cos(latRad); + double t = s / c; + double eta = e2Squared * (c * c); + double sn = sphsn(a, eSquared, latRad); + tmd = sphtmd(ap, bp, cp, dp, ep, latRad); + double t1, t2, t3, t4, t5, t6, t7, t8, t9; + t1 = tmd * ok_new; + t2 = sn * s * c * ok_new / 2.0; + t3 = sn * s * (c * c * c) * ok_new * (5.0 - (t * t) + 9.0 * eta + 4.0 * (eta * eta)) / 24.0; + t4 = sn * s * (c * c * c * c * c) * ok_new * (61.0 - 58.0 * (t * t) + (t * t * t * t) + 270.0 * eta - 330.0 * (t * t) * eta + 445.0 * (eta * eta) + 324.0 * (eta * eta * eta) - 680.0 * (t * t) * (eta * eta) + 88.0 * (eta * eta * eta * eta) - 600.0 * (t * t) * (eta * eta * eta) - 192.0 * (t * t) * (eta * eta * eta * eta)) / 720.0; + t5 = sn * s * (c * c * c * c * c * c * c) * ok_new * (1385.0 - 3111.0 * (t * t) + 543.0 * (t * t * t * t) - (t * t * t * t * t * t)) / 40320.0; + puwgCoord.northing = nfn + t1 + (dlam * dlam) * t2 + (dlam * dlam * dlam * dlam) * t3 + (dlam * dlam * dlam * dlam * dlam * dlam) * t4 + (dlam * dlam * dlam * dlam * dlam * dlam * dlam * dlam) * t5; + t6 = sn * c * ok_new; + t7 = sn * (c * c * c) * ok_new * (1.0 - (t * t) + eta) / 6.0; + t8 = sn * (c * c * c * c * c) * ok_new * (5.0 - 18.0 * (t * t) + (t * t * t * t) + 14.0 * eta - 58.0 * (t * t) * eta + 13.0 * (eta * eta) + 4.0 * (eta * eta * eta) - 64.0 * (t * t) * (eta * eta) - 24.0 * (t * t) * (eta * eta * eta)) / 120.0; + t9 = sn * (c * c * c * c * c * c * c) * ok_new * (61.0 - 479.0 * (t * t) + 179.0 * (t * t * t * t) - (t * t * t * t * t * t)) / 5040.0; + puwgCoord.easting = fe + strf + dlam * t6 + (dlam * dlam * dlam) * t7 + (dlam * dlam * dlam * dlam * dlam) * t8 + (dlam * dlam * dlam * dlam * dlam * dlam * dlam) * t9;// + 0.5; + } + + + //======================================================================= +// Funkcja do konwersji współrzędnych płaskich X/Y UTM na elipsoidalne lat/lon (dla dowolnej elipsoidy) +//======================================================================= +// Wymagania: +// ------------------------------------- +// utmXZone musi być wartością w garanicach od 1 do 60 +// utmYZone musi być jedną z liter: CDEFGHJKLMNPQRSTUVWX +// Argumenty wejściowe i wyjściowe: +// ------------------------------------ +// double a: długość dużej półosi, w metrach (np. dla elipsoidy WGS 84, 6378137.0) +// double f: spłaszczenie elipsoidalne (np. dla elipsoidy WGS 84, 1 / 298.257223563) +// int utmXZone: nr strefy UTM wg. podziału południkowego (zwracane numery od 1 do 60, każda strefa ma sześć stopni) +// char utmYZone: nr strefy wg. podziału równoleżnikowego (zwracane wartości: CDEFGHJKLMNPQRSTUVWX) +// double easting, double northing: współrzędna X, Y UTM do konwersji [metry] +// double& lat, double& lon: współrzędne elipsoidalne lat/lon po konwersji [stopnie] +//======================================================================= + public static void convertUtmToLatLon(UTMCoord utmCoord, GeoCoord geoCoord) { + double nfn; + utmCoord.yZone = Character.toUpperCase(utmCoord.yZone); + if (utmCoord.yZone <= 'M' && utmCoord.yZone >= 'C') { + nfn = 10000000.0; + } else { + nfn = 0; + } + double tmd = (utmCoord.northing - nfn) / ok; + double sr = sphsr(a, eSquared, 0.0); + double ftphi = tmd / sr; + double t10, t11, t12, t13, t14, t15, t16, t17; + for (int i = 0; i < 5; i++) { + t10 = sphtmd(ap, bp, cp, dp, ep, ftphi); + sr = sphsr(a, eSquared, ftphi); + ftphi = ftphi + (tmd - t10) / sr; + } + sr = sphsr(a, eSquared, ftphi); + double sn = sphsn(a, eSquared, ftphi); + double s = Math.sin(ftphi); + double c = Math.cos(ftphi); + double t = s / c; + double eta = e2Squared * (c * c); + double de = utmCoord.easting - fe; + t10 = t / (2.0 * sr * sn * (ok * ok)); + t11 = t * (5.0 + 3.0 * (t * t) + eta - 4.0 * (eta * eta) - 9.0 * (t * t) * eta) / (24.0 * sr * (sn * sn * sn) * (ok * ok * ok * ok)); + t12 = t * (61.0 + 90.0 * (t * t) + 46.0 * eta + 45.0 * (t * t * t * t) - 252.0 * (t * t) * eta - 3.0 * (eta * eta) + 100.0 * (eta * eta * eta) - 66.0 * (t * t) * (eta * eta) - 90.0 * (t * t * t * t) * eta + 88.0 * (eta * eta * eta * eta) + 225.0 * (t * t * t * t) * (eta * eta) + 84.0 * (t * t) * (eta * eta * eta) - 192.0 * (t * t) * (eta * eta * eta * eta)) / (720.0 * sr * (sn * sn * sn * sn * sn) * (ok * ok * ok * ok * ok * ok)); + t13 = t * (1385.0 + 3633 * (t * t) + 4095.0 * (t * t * t * t) + 1575.0 * (t * t * t * t * t * t)) / (40320 * sr * (sn * sn * sn * sn * sn * sn * sn) * (ok * ok * ok * ok * ok * ok * ok * ok)); + geoCoord.lat = ftphi - (de * de) * t10 + (de * de * de * de) * t11 - (de * de * de * de * de * de) * t12 + (de * de * de * de * de * de * de * de) * t13; + t14 = 1.0 / (sn * c * ok); + t15 = (1.0 + 2.0 * (t * t) + eta) / (6.0 * (sn * sn * sn) * c * (ok * ok * ok)); + t16 = 1.0 * (5.0 + 6.0 * eta + 28.0 * (t * t) - 3.0 * (eta * eta) + 8.0 * (t * t) * eta + 24.0 * (t * t * t * t) - 4.0 * (eta * eta * eta) + 4.0 * (t * t) * (eta * eta) + 24.0 * (t * t) * (eta * eta * eta)) / (120.0 * (sn * sn * sn * sn * sn) * c * (ok * ok * ok * ok * ok)); + t17 = 1.0 * (61.0 + 662.0 * (t * t) + 1320.0 * (t * t * t * t) + 720.0 * (t * t * t * t * t * t)) / (5040.0 * (sn * sn * sn * sn * sn * sn * sn) * c * (ok * ok * ok * ok * ok * ok * ok)); + double dlam = de * t14 - (de * de * de) * t15 + (de * de * de * de * de) * t16 - (de * de * de * de * de * de * de) * t17; + double olam = (utmCoord.xZone * 6 - 183.0) * DEG_2_RAD; + geoCoord.lon = olam + dlam; + geoCoord.lon *= RAD_2_DEG; + geoCoord.lat *= RAD_2_DEG; + } + + + // Współczynnik zniekształcenia skali mapy w południku osiowym dla odwzorowania kartograficznego UTM + private static final double ok = 0.9996; + private static final double DEG_2_RAD = Math.PI / 180.0; + private static final double RAD_2_DEG = 180.0 / Math.PI; + /** + * double a: dlługość dużej półsi, w metrach dla elipsoidy WGS-84, 6378137.0 + */ + private static final double a = 6378137.0; + + private static final double recf = 298.257223563; + /** + * double f: spłaszczenie elipsoidalne dla elipsoidy WGS-84, 1 / 298.257223563 + */ + private static final double f = 1.0 / recf; + // private static final double b = a * (recf - 1) / recf; + private static final double b = a * (1.0 - f); + private static final double eSquared = calculateESquared(a, b); + private static final double e2Squared = calculateE2Squared(a, b); + private static final double tn = (a - b) / (a + b); + private static final double ap = a * (1.0 - tn + 5.0 * ((tn * tn) - (tn * tn * tn)) / 4.0 + 81.0 * ((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 64.0); + private static final double bp = 3.0 * a * (tn - (tn * tn) + 7.0 * ((tn * tn * tn) - (tn * tn * tn * tn)) / 8.0 + 55.0 * (tn * tn * tn * tn * tn) / 64.0) / 2.0; + private static final double cp = 15.0 * a * ((tn * tn) - (tn * tn * tn) + 3.0 * ((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 4.0) / 16.0; + private static final double dp = 35.0 * a * ((tn * tn * tn) - (tn * tn * tn * tn) + 11.0 * (tn * tn * tn * tn * tn) / 16.0) / 48.0; + private static final double ep = 315.0 * a * ((tn * tn * tn * tn) - (tn * tn * tn * tn * tn)) / 512.0; + + /** + * Funkcja do konwersji współrzędnych płaskich X/Y odwzorowania kartograficznego 1992 i 2000 na elipsoidalne lat/lon elipsoide WGS84. + *

+ * PUWGCoord.proj: odwzorowanie kartograficzne (proj = 1 odpowiada odwzorowaniu 1992, natomiast każda inna odwzorowaniu 2000) + * + * @param puwgCoord współrzędne odwzorowania kartograficznego PUWG-1992 lub PUWG-2000 do konwersji [metry] + * @param geoCoord współrzędne geograficzne odwzorowania WGS-84 po konwersji [stopnie] + */ + public static void convertPuwgToLatLon(PUWGCoord puwgCoord, GeoCoord geoCoord) { + double ok = (puwgCoord.proj != 1) ? 0.999923 : 0.9993; + double nfn; + double tmd; + double ftphi; + double eta; + double dlam; + double olam = 0.0; + double strf = 0.0; + + if (puwgCoord.proj == 1) { + olam = 19.0 * DEG_2_RAD; + strf = 0.0; + nfn = -5300000.0; + } else { + nfn = 0; + if (puwgCoord.easting < 6000000.0 && puwgCoord.easting > 5000000.0) { + strf = 5000000.0; + olam = 15.0 * DEG_2_RAD; + } + if (puwgCoord.easting < 7000000.0 && puwgCoord.easting > 6000000.0) { + strf = 6000000.0; + olam = 18.0 * DEG_2_RAD; + } + if (puwgCoord.easting < 8000000.0 && puwgCoord.easting > 7000000.0) { + strf = 7000000.0; + olam = 21.0 * DEG_2_RAD; + } + if (puwgCoord.easting < 9000000.0 && puwgCoord.easting > 8000000.0) { + strf = 8000000.0; + olam = 24.0 * DEG_2_RAD; + } + } + tmd = (puwgCoord.northing - nfn) / ok; + double sr = sphsr(a, eSquared, 0.0); + ftphi = tmd / sr; + double t10, t11, t12, t13, t14, t15, t16, t17; + for (int i = 0; i < 5; i++) { + t10 = sphtmd(ap, bp, cp, dp, ep, ftphi); + sr = sphsr(a, eSquared, ftphi); + ftphi = ftphi + (tmd - t10) / sr; + } + sr = sphsr(a, eSquared, ftphi); + double sn = sphsn(a, eSquared, ftphi); + double sn_pow_2 = sn * sn; + double sn_pow_3 = sn_pow_2 * sn; + double sn_pow_4 = sn_pow_3 * sn; + double sn_pow_5 = sn_pow_4 * sn; + double sn_pow_7 = sn_pow_5 * sn_pow_2; + double s = Math.sin(ftphi); + double c = Math.cos(ftphi); + double t = s / c; + double t_pow_2 = t * t; + double t_pow_4 = t_pow_2 * t_pow_2; + double t_pow_6 = t_pow_4 * t_pow_2; + eta = e2Squared * (c * c); + double eta_pow_2 = eta * eta; + double eta_pow_3 = eta_pow_2 * eta; + double eta_pow_4 = eta_pow_2 * eta_pow_2; + double de = puwgCoord.easting - fe - strf; + double de_pow_2 = de * de; + double de_pow_3 = de_pow_2 * de; + double de_pow_4 = de_pow_3 * de; + t10 = t / (2.0 * sr * sn * (ok * ok)); + t11 = 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 * ok * ok * ok)); + t12 = 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 * ok * ok * ok * ok * ok)); + t13 = t * (1385.0 + 3633 * t_pow_2 + 4095.0 * t_pow_4 + 1575.0 * t_pow_6) / (40320 * sr * sn_pow_7 * (ok * ok * ok * ok * ok * ok * ok * ok)); + geoCoord.lat = ftphi - de_pow_2 * t10 + de_pow_4 * t11 - de_pow_3 * de_pow_3 * t12 + de_pow_4 * de_pow_3 * t13; + t14 = 1.0 / (sn * c * ok); + t15 = (1.0 + 2.0 * t_pow_2 + eta) / (6.0 * sn_pow_3 * c * (ok * ok * ok)); + t16 = 1.0 * (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 * ok * ok * ok * ok)); + t17 = 1.0 * (61.0 + 662.0 * t_pow_2 + 1320.0 * t_pow_4 + 720.0 * t_pow_6) / (5040.0 * sn_pow_7 * c * (ok * ok * ok * ok * ok * ok * ok)); + dlam = de * t14 - de_pow_3 * t15 + de_pow_3 * de_pow_2 * t16 - de_pow_3 * de_pow_4 * t17; + geoCoord.lon = olam + dlam; + geoCoord.lon *= RAD_2_DEG; + geoCoord.lat *= RAD_2_DEG; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/DaneWysok.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/DaneWysok.java new file mode 100644 index 0000000..006e5ab --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/DaneWysok.java @@ -0,0 +1,21 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import pl.wat.ms4ds.terenfunkcje.GridCoord; + +/** + * + */ +public class DaneWysok { + + GridCoord idKw; + + double suma; + + int licz; + + public DaneWysok(GridCoord idKw, double suma, int licz) { + this.idKw = idKw; + this.suma = suma; + this.licz = licz; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EAreaFeature.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EAreaFeature.java new file mode 100644 index 0000000..df44fc8 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EAreaFeature.java @@ -0,0 +1,9 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EAreaFeature { + + FOREST, + SWAMP, + WATER, + BUILDINGS +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/ELinearFeature.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/ELinearFeature.java new file mode 100644 index 0000000..14da0de --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/ELinearFeature.java @@ -0,0 +1,8 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum ELinearFeature { + + ROAD, + WATER_WAY, + DITCH +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMAmenity.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMAmenity.java new file mode 100644 index 0000000..51d121b --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMAmenity.java @@ -0,0 +1,47 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMAmenity { + + /** + * v="hospital" | v="school" | v="post_office" | v="police" | v="marketplace" | v="fire_station" | + * v="theatre" | v="cinema" | v="pharmacy" | v="nursing_home" | v="bank" | v="fuel" + * v="university" | v="library" | v="clinic" + */ + HOSPITAL, + SCHOOL, + POST_OFFICE, + POLICE, + MARKETPLACE, + FIRE_STATION, + THEATRE, + CINEMA, + PHARMACY, + NURSING_HOME, + BANK, + FUEL, + UNIVERSITY, + LIBRARY, + CLINIC; + + public static EOSMAmenity getValue(String str) { + switch (str) { + case "hospital": return HOSPITAL; + case "school": return SCHOOL; + case "post_office": return POST_OFFICE; + case "police": return POLICE; + case "marketplace": return MARKETPLACE; + case "fire_station": return FIRE_STATION; + case "theatre": return THEATRE; + case "cinema": return CINEMA; + case "pharmacy": return PHARMACY; + case "nursing_home": return NURSING_HOME; + case "bank": return BANK; + case "fuel": return FUEL; + case "university": return UNIVERSITY; + case "library": return LIBRARY; + case "clinic": return CLINIC; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBridge.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBridge.java new file mode 100644 index 0000000..2c8008e --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBridge.java @@ -0,0 +1,19 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMBridge { + + /** + * v="yes" | "viaduct" + */ + YES, + VIADUCT; + + public static EOSMBridge getValue(String str) { + switch (str) { + case "yes": return YES; + case "viaduct": return VIADUCT; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBuilding.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBuilding.java new file mode 100644 index 0000000..6e2ed96 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMBuilding.java @@ -0,0 +1,29 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMBuilding { + + /** + * v="yes" | v="chapel" | v="church" | v="house" | v="office" | v="manufacture" | v="industrial" + */ + YES, + CHAPEL, + CHURCH, + HOUSE, + OFFICE, + MANUFACTURE, + INDUSTRIAL; + + + public static EOSMBuilding getValue(String str) { + switch (str) { + case "yes": return YES; + case "chapel": return CHAPEL; + case "church": return CHURCH; + case "house": return HOUSE; + case "office": return OFFICE; + case "manufacture": return MANUFACTURE; + case "industrial": return INDUSTRIAL; + default: return null; + } + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMHighway.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMHighway.java new file mode 100644 index 0000000..3ee1995 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMHighway.java @@ -0,0 +1,50 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMHighway { + + /** + * v="motorway" | v="trunk" | v="primary" | v="secondary" | v="tertiary" | v="unclassified" + * | v="residential" | v="pedestrian" | v="track" + * | v="motorway_link" | v="trunk_link" | v="primary_link" | v="secondary_link" | v="tertiary_link" + * | v="living_street" | v="service" + */ + MOTORWAY, + TRUNCK, + PRIMARY, + SECONDARY, + TERTIARY, + UNCLASSIFIED, + RESIDENTIAL, + PEDESTRIAN, + TRACK, + MOTORWAY_LINK, + TRUNCK_LINK, + PRIMARY_LINK, + SECONDARY_LINK, + TERTIARY_LINK, + LIVING_STREET, + SERVICE; + + public static EOSMHighway getValue(String str) { + switch (str) { + case "motorway": return MOTORWAY; + case "trunk": return TRUNCK; + case "primary": return PRIMARY; + case "secondary": return SECONDARY; + case "tertiary": return TERTIARY; + case "unclassified": return UNCLASSIFIED; + case "residential": return RESIDENTIAL; + case "pedestrian": return PEDESTRIAN; + case "track": return TRACK; + case "motorway_link": return MOTORWAY_LINK; + case "trunk_link": return TRUNCK_LINK; + case "primary_link": return PRIMARY_LINK; + case "secondary_link": return SECONDARY_LINK; + case "tertiary_link": return TERTIARY_LINK; + case "living_street": return LIVING_STREET; + case "service": return SERVICE; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLandcover.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLandcover.java new file mode 100644 index 0000000..d7a0e54 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLandcover.java @@ -0,0 +1,21 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMLandcover { + + /** + * v="trees" | v="water" | v="grass" + */ + TREES, + WATER, + GRASS; + + public static EOSMLandcover getValue(String str) { + switch (str) { + case "trees": return TREES; + case "water": return WATER; + case "grass": return GRASS; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLanduse.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLanduse.java new file mode 100644 index 0000000..4406bc0 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMLanduse.java @@ -0,0 +1,21 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMLanduse { + + /** + * v="forest" | v="residential" | v="commercial" + */ + FOREST, + RESIDENTIAL, + COMMERCIAL; + + public static EOSMLanduse getValue(String str) { + switch (str) { + case "forest": return FOREST; + case "residential": return RESIDENTIAL; + case "commercial": return COMMERCIAL; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMNatural.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMNatural.java new file mode 100644 index 0000000..d4e28f2 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMNatural.java @@ -0,0 +1,23 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMNatural { + + /** + * v="water" | v="reservoir" | v="wetland" | v="wood" + */ + WATER, + RESERVOIR, + WETLAND, + WOOD; + + public static EOSMNatural getValue(String str) { + switch (str) { + case "water": return WATER; + case "reservoir": return RESERVOIR; + case "wetland": return WETLAND; + case "wood": return WOOD; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWater.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWater.java new file mode 100644 index 0000000..4086402 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWater.java @@ -0,0 +1,29 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMWater { + + /** + * v="lake" | v="reservoir" | v="river" | v="canal" | v="cove" | v="lagoon" | v="pond" + */ + LAKE, + RESERVOIR, + RIVER, + CANAL, + COVE, + LAGOON, + POND; + + public static EOSMWater getValue(String str) { + switch (str) { + case "lake": return LAKE; + case "reservoir": return RESERVOIR; + case "river": return RIVER; + case "canal": return CANAL; + case "cove": return COVE; + case "lagoon": return LAGOON; + case "pond": return POND; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWaterway.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWaterway.java new file mode 100644 index 0000000..422290b --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EOSMWaterway.java @@ -0,0 +1,27 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +public enum EOSMWaterway { + + /** + * v="river" | v="riverbank" | v="stream" | v="canal" | v="drain" | v="ditch" + */ + RIVER, + RIVERBANK, + STREAM, + CANAL, + DRAIN, + DITCH; + + public static EOSMWaterway getValue(String str) { + switch (str) { + case "river": return RIVER; + case "riverbank": return RIVERBANK; + case "stream": return STREAM; + case "canal": return CANAL; + case "drain": return DRAIN; + case "ditch": return DITCH; + default: return null; + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EsriFileReader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EsriFileReader.java new file mode 100644 index 0000000..867f724 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/EsriFileReader.java @@ -0,0 +1,210 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; +import org.nocrala.tools.gis.data.esri.shapefile.ShapeFileReader; +import org.nocrala.tools.gis.data.esri.shapefile.ValidationPreferences; +import org.nocrala.tools.gis.data.esri.shapefile.exception.InvalidShapeFileException; +import org.nocrala.tools.gis.data.esri.shapefile.header.ShapeFileHeader; +import org.nocrala.tools.gis.data.esri.shapefile.shape.AbstractShape; +import org.nocrala.tools.gis.data.esri.shapefile.shape.PointData; +import org.nocrala.tools.gis.data.esri.shapefile.shape.shapes.MultiPointZShape; +import org.nocrala.tools.gis.data.esri.shapefile.shape.shapes.PointShape; +import org.nocrala.tools.gis.data.esri.shapefile.shape.shapes.PolygonShape; +import org.nocrala.tools.gis.data.esri.shapefile.shape.shapes.PolylineShape; +import pl.wat.ms4ds.terenfunkcje.GridCoord; +import pl.wat.ms4ds.terenfunkcje.MapConsts; +import pl.wat.ms4ds.terenfunkcje.Teren; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; + +public class EsriFileReader { + private static final Logger LOGGER = Logger.getLogger(EsriFileReader.class); + + public static void main(String[] args) throws IOException, InvalidShapeFileException { + String fn = "C:/Workspace/osm/dolnoslaskie-251217-free.shp/gis_osm_water_a_free_1.shp"; + if (args.length > 0) { + fn = args[0]; + } + FileInputStream is = new FileInputStream(fn); + ValidationPreferences prefs = new ValidationPreferences(); + prefs.setMaxNumberOfPointsPerShape(50000); +// ShapeFileReader r = new ShapeFileReader(is); + ShapeFileReader r = new ShapeFileReader(is, prefs); + + ShapeFileHeader h = r.getHeader(); +// System.out.println("The shape type of this files is " + h.getShapeType()); + + HashMap relationsMap = new HashMap<>(); + HashMap waysMap = new HashMap<>(); + + LOGGER.debug("Poczatek odczytu danych o pokryciu wodami z pliku: " + fn); + + int count = 0; + AbstractShape s; + int relId = 0; + int wayId = 0; + int nodeId = 0; + while ((s = r.next()) != null) { + count++; + switch (s.getShapeType()) { + case POINT: + PointShape aPoint = (PointShape) s; + // Do something with the point shape... + break; + case MULTIPOINT_Z: + MultiPointZShape aMultiPointZ = (MultiPointZShape) s; + // Do something with the MultiPointZ shape... + break; + case POLYLINE: + if (s instanceof PolylineShape) { + PolylineShape aPolyline = (PolylineShape) s; + if (aPolyline.getBoxMinX() > 10 && aPolyline.getBoxMaxX() < 30 + && aPolyline.getBoxMinY() > 40 && aPolyline.getBoxMaxY() < 58) { + + } + } + + break; + case POLYGON: + PolygonShape aPolygon = (PolygonShape) s; + if (aPolygon.getBoxMinX() > 10 && aPolygon.getBoxMaxX() < 30 + && aPolygon.getBoxMinY() > 40 && aPolygon.getBoxMaxY() < 58) { +// System.out.println("I read a Polygon with " +// + aPolygon.getNumberOfParts() + " parts and " +// + aPolygon.getNumberOfPoints() + " points"); + Relation currRelation = new Relation("" + relId); + currRelation.natural = EOSMNatural.WATER; + relId++; + for (int i = 0; i < aPolygon.getNumberOfParts(); i++) { + PointData[] points = aPolygon.getPointsOfPart(i); + Way currWay = new Way("" + wayId); + wayId++; + boolean jest_wezel_z_obszaru = false; + for (int j = 0; j < points.length; j++) { + Node currNode = new Node("" + nodeId); + nodeId++; + currNode.lon = points[j].getX() + 180; + currNode.lat = points[j].getY() + 90; + if (currNode.lon >= MapConsts.X_REF && currNode.lon <= MapConsts.X_REF + MapConsts.DX_REF + && currNode.lat >= MapConsts.Y_REF && currNode.lat <= MapConsts.Y_REF + MapConsts.DY_REF) { + jest_wezel_z_obszaru = true; + } + currNode.lon = Math.max(currNode.lon, MapConsts.X_REF); + currNode.lon = Math.min(currNode.lon, MapConsts.X_REF + MapConsts.DX_REF); + currNode.lon -= 180; + currNode.lat = Math.max(currNode.lat, MapConsts.Y_REF); + currNode.lat = Math.min(currNode.lat, MapConsts.Y_REF + MapConsts.DY_REF); + currNode.lat -= 90; + + currNode.idX = GridCoord.zamienDlugoscGeoNaIdKwadratuX(currNode.lon); + currNode.idY = GridCoord.zamienSzerokoscGeoNaIdKwadratuY(currNode.lat); + if (currNode.idX > 0 || currNode.idY > 0) { + currWay.nodes.add(currNode); + } + } + if (jest_wezel_z_obszaru) { + boolean clockwise = clockwiseOrientedPolygon(points); + if (clockwise) { + if (currWay.nodes.size() > 0) { + currRelation.outerWays.add(currWay); + currWay.natural = EOSMNatural.WATER; + } + } else { + currRelation.innerWays.add(currWay); + } + } +// String orient = clockwise ? clockwiseStr : counterclockwiseStr; +// System.out.println("- part " + i + " has " + points.length +// + " points. - orientation= " + orient); + } + if (currRelation.outerWays.size() > 0) { + relationsMap.put(currRelation.id, currRelation); + } + } + break; + default: +// System.out.println("Read other type of shape."); + } + if (count % 1000 == 0) { + LOGGER.debug("shape count= " + count); + } + } +// System.out.println("Total shapes read: " + total); + is.close(); + LOGGER.debug("Koniec odczytu danych o pokryciu wodami z pliku: " + fn); + try { + LOGGER.debug("Poczatek przetwarzania danych o pokryciu wodami"); + OpenStreetMapReader.generujDaneTerenowe("wody", relationsMap, waysMap, null); + LOGGER.debug("Koniec przetwarzania danych o pokryciu wodami"); + } catch (Exception e) { + e.printStackTrace(); + } + for (int i = 0; i < Worker.workers.size(); i++) { + try { + Worker.workers.get(i).join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + LOGGER.debug("Poczatek zapisu danych o pokryciu wodami"); + Teren.zapisBuforaMapyDoPliku(); +// Teren.setBinarnyFormatPliku(false); +// Teren.zapisBuforaMapyDoPliku(); + Teren.reset(); + LOGGER.debug("Koniec zapisu danych o pokryciu wodami"); + } + + public static boolean clockwiseOrientedPolygon(PointData[] points) { + PointData[] vertices = new PointData[points.length - 1]; + for (int i = 0; i < vertices.length; i++) { + vertices[i] = points[i]; + } + int smallest = 0; + for (int i = 1; i < vertices.length; i++) { + if (lexicographicalCompare(vertices[i].getX(), vertices[i].getY(), + vertices[smallest].getX(), vertices[smallest].getY()) == -1) { + smallest = i; + } + } + PointData center = vertices[smallest]; + PointData first = vertices[(smallest - 1 + vertices.length) % vertices.length]; + PointData last = vertices[(smallest + 1) % vertices.length]; + // first == a, center == b, last == c + // (xb - xa) * (yc - ya) - (xc - xa) * (yb - ya) + double det = (center.getX() - first.getX()) * (last.getY() - first.getY()) + - (last.getX() - first.getX()) * (center.getY() - first.getY()); + + return (det < 0); + } + + /** + * Porównuje dwa punkty leksykograficznie. + *

Jeżeli (x1, y1) mniejsze od (x2, y2) to zwraca -1 + *

Jeżeli (x1, y1) rowne (x2, y2) to zwraca 0 + *

Jeżeli (x1, y1) wieksze od (x2, y2) to zwraca 1 + * + * @param x1 współrzędna x pierwszego punktu + * @param y1 współrzędna y pierwszego punktu + * @param x2 współrzędna x drugiego punktu + * @param y2 współrzędna y drugiego punktu + * @return -1 gdy pierwszy punkt mniejszy, 0 gdy punkty rowne, 1 gdy pierwszy punkt wiekszy + */ + public static int lexicographicalCompare(double x1, double y1, double x2, double y2) { + if (x1 < x2) { + return -1; + } + if (x1 > x2) { + return 1; + } + if (y1 < y2) { + return -1; + } + if (y1 > y2) { + return 1; + } + return 0; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/MapBounds.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/MapBounds.java new file mode 100644 index 0000000..8681bd4 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/MapBounds.java @@ -0,0 +1,11 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +/** + * + */ +public class MapBounds { + double minLat; + double minLon; + double maxLat; + double maxLon; +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Node.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Node.java new file mode 100644 index 0000000..6b8cdaa --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Node.java @@ -0,0 +1,107 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import pl.wat.ms4ds.terenfunkcje.Kwadrat; +import pl.wat.ms4ds.terenfunkcje.MapConsts; +import pl.wat.ms4ds.terenfunkcje.Teren; + +/** + * + */ +public class Node { + String id; + double lon = 0; // x + double lat = 0; // y + int idX = -1; + int idY = -1; + int buildingsCount = 0; + +// GridCoord idKw; + + /** + * k="amenity" - v="hospital" | v="school" | v="post_office" | v="police" | v="marketplace" | v="fire_station" | + * v="theatre" | v="cinema" | v="pharmacy" | v="nursing_home" | v="bank" | v="fuel" + * v="university" | v="library" + */ + public EOSMAmenity amenity; + + /** + * k="building" - v="yes" | v="chapel" | v="church" | v="house" | v="office" | v="manufacture" | v="industrial" + */ + public EOSMBuilding building; + + /** + * k="bridge" - v="yes" | "viaduct" + */ + public EOSMBridge bridge; + + public Node(String id) { + this.id = id; + } + + public Node(Node oryg) { + this.id = oryg.id; + lon = oryg.lon; + lat = oryg.lat; + idX = oryg.idX; + idY = oryg.idY; + } + + static final int BUILDINGS_COUNT; + static { + switch (MapConsts.DL_MK) { + case 200: + BUILDINGS_COUNT = 8; + break; + case 100: + BUILDINGS_COUNT = 4; + break; + case 50: + BUILDINGS_COUNT = 2; + break; + case 25: + BUILDINGS_COUNT = 1; + break; + default: + BUILDINGS_COUNT = 1; + break; + } + } + + public void writeAreaFeatureIntoSquare(EAreaFeature type) { + if (buildingsCount >= BUILDINGS_COUNT) { + Kwadrat kw = Teren.getKwadrat(idX, idY); + kw.setStopienZabudowy(1.0f); + } else if (buildingsCount > 0) { + Kwadrat kw = Teren.getKwadrat(idX, idY); + kw.setStopienZabudowy(0.5f); + } + } + + @Override + public String toString() { + return "Node{" + + "id=" + id + '\'' + + ", lat='" + lat + '\'' + + ", lon='" + lon + '\'' + + ", idKw=(" + idX + ", " + idY + ")}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Node node = (Node) o; + + if (idX != node.idX) return false; + return idY == node.idY; + + } + + @Override + public int hashCode() { + int result = idX; + result = 31 * result + idY; + return result; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/OpenStreetMapReader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/OpenStreetMapReader.java new file mode 100644 index 0000000..adec1a8 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/OpenStreetMapReader.java @@ -0,0 +1,979 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.terenfunkcje.GridCoord; +import pl.wat.ms4ds.terenfunkcje.Teren; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import javax.xml.stream.*; + +/** + * + */ +public class OpenStreetMapReader { + private static final Logger LOGGER = Logger.getLogger(OpenStreetMapReader.class); + + private static final String DATA_DIR = "c:/Workspace/openstreetmap-data-20160925/"; + + static final Object synch = new Object(); + + public static void main(String[] args) throws Exception { + System.out.println("OpenStreetMapReader - Working Directory = " + + System.getProperty("user.dir")); + String osm_fn = DATA_DIR + args[0] + "-latest.osm"; + HashMap relationsMap = new HashMap<>(8000); + HashMap relationWaysMap = new HashMap<>(50000); + HashMap nodesMap = new HashMap<>(5000000); + HashMap buildingNodesMap = new HashMap<>(500000); + HashMap waysMap = new HashMap<>(800000); + String[] goals = {"lasy", "wody", "bagna", "zabudowa", "drogi", "rzeki", "rowy"}; + ArrayList toDo = new ArrayList<>(); + for (int i = 1; i < args.length; i++) { + for (int j = 0; j < goals.length; j++) { + if (goals[j].equals(args[i])) { + toDo.add(goals[j]); + break; + } + } + } + MapBounds mapBounds = new MapBounds(); +// readMapBounds(mapBounds, osm_fn); + for (int i = 0; i < toDo.size(); i++) { + relationsMap.clear(); + relationWaysMap.clear(); + nodesMap.clear(); + buildingNodesMap.clear(); + waysMap.clear(); + readRelations(relationsMap, relationWaysMap, osm_fn, toDo.get(i)); + readWays(nodesMap, relationWaysMap, waysMap, osm_fn, toDo.get(i)); + readNodes(nodesMap, buildingNodesMap, mapBounds, osm_fn, toDo.get(i)); + if (nodesMap.size() > 0) { + String nodes_poland_fn = DATA_DIR + "poland-latest_nodes.osm"; + readNodes(nodesMap, null, mapBounds, nodes_poland_fn, toDo.get(i)); + } + for (Relation rel : relationsMap.values()) { + rel.outerWays = rel.generatePolygons(rel.outerWays); + rel.innerWays = rel.generatePolygons(rel.innerWays); + } + LOGGER.debug("Poczatek przetwarzania danych dla regionu " + osm_fn + " - cel: " + toDo.get(i)); + generujDaneTerenowe(toDo.get(i), relationsMap, waysMap, buildingNodesMap); + LOGGER.debug("Koniec przetwarzania danych dla regionu " + osm_fn + " - cel: " + toDo.get(i)); +// Teren.setBinarnyFormatPliku(false); +// Teren.zapisBuforaMapyDoPliku(); +// Teren.reset(); +// Teren.setBinarnyFormatPliku(true); + } + LOGGER.debug("Poczatek zapisu danych dla regionu " + osm_fn); + Teren.zapisBuforaMapyDoPliku(); + LOGGER.debug("Koniec zapisu danych dla regionu " + osm_fn); + } + + + // XMLOutputFactory outFactory = XMLOutputFactory.newInstance(); +// +// try { +// XMLStreamWriter writer = outFactory.createXMLStreamWriter(new FileWriter("au2data\\output2.xml")); +// +// writer.writeStartDocument(); +// writer.writeStartElement("document"); +// writer.writeStartElement("data"); +// writer.writeAttribute("name", "value"); +// writer.writeEndElement(); +// writer.writeEndElement(); +// writer.writeEndDocument(); +// +// writer.flush(); +// writer.close(); +// +// } catch (XMLStreamException e) { +// e.printStackTrace(); +// } catch (IOException e) { +// e.printStackTrace(); +// } + + private static void readMapBounds(MapBounds mapBounds, String fn) throws Exception { + XMLInputFactory factory = XMLInputFactory.newInstance(); +// FileInputStream is = new FileInputStream(fn); + FileInputStream is = new FileInputStream(fn); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); + XMLStreamReader reader = factory.createXMLStreamReader(is); + String val; + while (reader.hasNext()) { + int eventType = reader.next(); + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + if ("bounds".equals(reader.getLocalName())) { + for (int i = 0; i < reader.getAttributeCount(); i++) { + val = reader.getAttributeLocalName(i); + switch (val) { + case "minlat": + val = reader.getAttributeValue(i); + mapBounds.minLat = Double.parseDouble(val); + break; + case "minlon": + val = reader.getAttributeValue(i); + mapBounds.minLon = Double.parseDouble(val); + break; + case "maxlat": + val = reader.getAttributeValue(i); + mapBounds.maxLat = Double.parseDouble(val); + break; + case "maxlon": + val = reader.getAttributeValue(i); + mapBounds.maxLon = Double.parseDouble(val); + break; + default: + } + } + reader.close(); + return; + } + break; + + default: + break; + } + } + reader.close(); + } + + private static void readNodes(HashMap nodesMap, HashMap buildingNodesMap, MapBounds mapBounds, String fn, String genGoal) throws Exception { + Node currNode = null; + XMLInputFactory factory = XMLInputFactory.newInstance(); + FileInputStream is = new FileInputStream(fn); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); + XMLStreamReader reader = factory.createXMLStreamReader(is); + String val; + int count = 1; + LOGGER.debug("Poczatek odczytu wezlow z pliku: " + fn); + while (reader.hasNext()) { + int eventType = reader.next(); + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + if ("node".equals(reader.getLocalName())) { + val = reader.getAttributeValue(0); +// if ("3206180317".equals(val)) { +// logger.debug("!!!!!!!!!!! niepoprawny wezel id= " + val); +// } + if (count % 100000 == 0) { + LOGGER.debug("+-+-+-+-+-+-+-+- liczba odczytanych wezlow= " + count / 1000 + " [tys.], ostatni wezel id= " + val); + } + currNode = nodesMap.remove(val); + if (!"zabudowa".equals(genGoal)) { + if (currNode != null) { + int c = 0; + for (int i = 1; i < reader.getAttributeCount() && c < 2; i++) { + val = reader.getAttributeLocalName(i); + switch (val) { + case "lat": + val = reader.getAttributeValue(i); + currNode.lat = Double.parseDouble(val); + c++; + break; + case "lon": + val = reader.getAttributeValue(i); + currNode.lon = Double.parseDouble(val); + c++; + break; + default: + } + } + } + } else if (buildingNodesMap != null) { + if (currNode == null) { + currNode = buildingNodesMap.get(val); + if (currNode == null) { + currNode = new Node(val); + } + } + int c = 0; + for (int i = 1; i < reader.getAttributeCount() && c < 2; i++) { + val = reader.getAttributeLocalName(i); + switch (val) { + case "lat": + val = reader.getAttributeValue(i); + currNode.lat = Double.parseDouble(val); + c++; + break; + case "lon": + val = reader.getAttributeValue(i); + currNode.lon = Double.parseDouble(val); + c++; + break; + default: + } + } + } + } else if ("tag".equals(reader.getLocalName())) { + if (currNode != null) { + String k = reader.getAttributeValue(0); + String v = reader.getAttributeValue(1); + switch (k) { + case "building": + currNode.building = EOSMBuilding.getValue(v); + break; + case "amenity": + currNode.amenity = EOSMAmenity.getValue(v); + break; + case "addr:housenumber": + currNode.buildingsCount++; + break; + default: + break; + } + } + } else if ("way".equals(reader.getLocalName())) { + reader.close(); + return; + } + break; + + case XMLStreamConstants.CHARACTERS: +// tagContent = reader.getText().trim(); + break; + + case XMLStreamConstants.END_ELEMENT: + switch (reader.getLocalName()) { + case "node": + if (currNode != null) { + currNode.idX = GridCoord.zamienDlugoscGeoNaIdKwadratuX(currNode.lon); + currNode.idY = GridCoord.zamienSzerokoscGeoNaIdKwadratuY(currNode.lat); + if ("zabudowa".equals(genGoal) && currNode.buildingsCount > 0) { + buildingNodesMap.put(currNode.id, currNode); + } + count++; + currNode = null; + } + break; + default: + break; + } + break; + + default: + break; + } + } + reader.close(); + } + + private static void readWays(HashMap nodesMap, HashMap relationWaysMap, + HashMap goalWaysMap, String fn, String genGoal) throws Exception { + Way currWay = null; + String tagContent = null; + XMLInputFactory factory = XMLInputFactory.newInstance(); +// FileInputStream is = new FileInputStream(fn); + FileInputStream is = new FileInputStream(fn); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); + XMLStreamReader reader = factory.createXMLStreamReader(is); + String val; + int count = 0; + LOGGER.debug("Poczatek odczytu lamanych z pliku: " + fn + " - cel: " + genGoal); + while (reader.hasNext()) { + int eventType = reader.next(); + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + if ("way".equals(reader.getLocalName())) { + val = reader.getAttributeValue(0); + currWay = relationWaysMap.get(val); + if (currWay == null) { + currWay = new Way(val); + } +// if ("314575560".equals(val)) { +// logger.debug("!!!!!"); +// } + count++; + if (count % 10000 == 0) { + LOGGER.debug("=================== liczba odczytanych lamanych= " + count / 1000 + " [tys.], ostatnia lamana id= " + val); + LOGGER.debug("=>liczba lamanych celu=" + goalWaysMap.size() + ", liczba lamanych relacji=" + relationWaysMap.size() + ", liczba wezlow=" + nodesMap.size()); + } + } else if ("nd".equals(reader.getLocalName())) { + if (currWay != null) { + // odczyt id wezla wchodzacego w sklad lamanej + val = reader.getAttributeValue(0); + Node node = nodesMap.get(val); + if (node == null) { + for (int i = 0; i < currWay.nodes.size(); i++) { + if (val.equals(currWay.nodes.get(i).id)) { + node = currWay.nodes.get(i); + break; + } + } + if (node == null) { + node = new Node(val); + } + } + currWay.nodes.add(node); + } + } else if ("tag".equals(reader.getLocalName())) { + if (currWay != null) { + String k = reader.getAttributeValue(0); + String v = reader.getAttributeValue(1); + switch (k) { + case "highway": + currWay.highway = EOSMHighway.getValue(v); + break; + case "waterway": + currWay.waterway = EOSMWaterway.getValue(v); + break; + case "natural": + currWay.natural = EOSMNatural.getValue(v); + break; + case "water": + currWay.water = EOSMWater.getValue(v); + break; + case "landuse": + currWay.landuse = EOSMLanduse.getValue(v); + break; + case "landcover": + currWay.landcover = EOSMLandcover.getValue(v); + break; + case "bridge": + currWay.bridge = EOSMBridge.getValue(v); + break; + case "building": + currWay.building = EOSMBuilding.getValue(v); + break; + case "amenity": + currWay.amenity = EOSMAmenity.getValue(v); + break; + default: + break; + } + } + } + break; + + case XMLStreamConstants.CHARACTERS: +// tagContent = reader.getText().trim(); + break; + + case XMLStreamConstants.END_ELEMENT: + if (currWay != null) { + switch (reader.getLocalName()) { + case "way": + if ("lasy".equals(genGoal)) { +// LASY + if (relationWaysMap.containsKey(currWay.id)) { + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if ((currWay.landuse != null) && currWay.landuse == EOSMLanduse.FOREST) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if ((currWay.landcover != null) && currWay.landcover == EOSMLandcover.TREES) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if ((currWay.natural != null) && currWay.natural == EOSMNatural.WOOD) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } + } else if ("wody".equals(genGoal)) { +// SZEROKIE RZEKI, JEZIORA I ZBIORNIKI WODNE + if (relationWaysMap.containsKey(currWay.id)) { + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if (currWay.natural != null && currWay.natural == EOSMNatural.WATER) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if (currWay.waterway != null && currWay.waterway == EOSMWaterway.RIVERBANK) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } + } else if ("drogi".equals(genGoal)) { +// DROGI + if (currWay.highway != null) { + switch (currWay.highway) { + case MOTORWAY: + case TRUNCK: + case PRIMARY: + case SECONDARY: + case TERTIARY: + case UNCLASSIFIED: + case RESIDENTIAL: + case PEDESTRIAN: + case TRACK: + case MOTORWAY_LINK: + case TRUNCK_LINK: + case PRIMARY_LINK: + case SECONDARY_LINK: + case TERTIARY_LINK: + case SERVICE: + case LIVING_STREET: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: +// case "footway": +// case "bridleway": +// case "steps": +// case "cycleway": +// case "PATH": +// case "proposed": + break; + } + } + } else if ("rzeki".equals(genGoal)) { +// RZEKI (LINIOWE, NIESZEROKIE) + if (currWay.waterway != null) { + switch (currWay.waterway) { + case RIVER: + case CANAL: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: + } + } + } else if ("rowy".equals(genGoal)) { +// ROWY + if (currWay.waterway != null) { + switch (currWay.waterway) { + case STREAM: + case DRAIN: + case DITCH: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: + } + } + } else if ("bagna".equals(genGoal)) { +// BAGNA + if (currWay.natural != null) { + switch (currWay.natural) { + case WETLAND: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: + } + } + } else if ("zabudowa".equals(genGoal)) { +// ZABUDOWA + if (currWay.landuse != null) { + switch (currWay.landuse) { + case RESIDENTIAL: + case COMMERCIAL: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: + } + } else if (currWay.building != null) { + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + } else if (currWay.amenity != null) { + switch (currWay.amenity) { + case HOSPITAL: + case CLINIC: + case POST_OFFICE: + case POLICE: + case FIRE_STATION: + case MARKETPLACE: + goalWaysMap.put(currWay.id, currWay); + for (Node node : currWay.nodes) { + nodesMap.put(node.id, node); + } + break; + default: + } + } + } + currWay = null; + break; + default: + break; + } + } + break; + + default: + } + } + reader.close(); + } + + private static void readRelations(HashMap relationsMap, HashMap relationWaysMap, + String fn, String genGoal) throws Exception { + Relation currRelation = null; + String tagContent = null; + if (!("lasy".equals(genGoal)) && !("wody".equals(genGoal))) { + return; + } +// fn += "-latest_relations.osm"; + XMLInputFactory factory = XMLInputFactory.newInstance(); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); +// BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(fn)); +// XMLStreamReader reader = factory.createXMLStreamReader(inputStream); + + FileInputStream is = new FileInputStream(fn); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); + XMLStreamReader reader = factory.createXMLStreamReader(is); + + String val; + int count = 0; + LOGGER.debug("Poczatek odczytu relacji z pliku: " + fn + " - cel: " + genGoal); + while (reader.hasNext()) { + int eventType = reader.next(); + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + if ("relation".equals(reader.getLocalName())) { + val = reader.getAttributeValue(0); + currRelation = new Relation(val); + count++; + if (count % 1000 == 0) { + LOGGER.debug("+-+-+-+-+-+-+-+- liczba odczytanych relacji= " + count / 1000 + " [tys.], ostatnia relacja id= " + val); + } + } else if ("member".equals(reader.getLocalName())) { + if (currRelation != null) { + String v = reader.getAttributeValue(0); + if ("way".equals(v)) { + // identyfikator łamanej + String v1 = reader.getAttributeValue(1); + // rola łamanej w relacji + String v2 = reader.getAttributeValue(2); + if ("outer".equals(v2)) { + Way way = new Way(v1); + if (way != null) { + currRelation.outerWays.add(way); + } + } else if ("inner".equals(v2)) { + Way way = new Way(v1); + if (way != null) { + currRelation.innerWays.add(way); + } + } + } + } + } else if ("tag".equals(reader.getLocalName())) { + if (currRelation != null) { + String k = reader.getAttributeValue(0); + String v = reader.getAttributeValue(1); + switch (k) { + case "waterway": + currRelation.waterway = EOSMWaterway.getValue(v); + break; + case "natural": + currRelation.natural = EOSMNatural.getValue(v); + break; + case "landuse": + currRelation.landuse = EOSMLanduse.getValue(v); + break; + case "landcover": + currRelation.landcover = EOSMLandcover.getValue(v); + break; + case "type": + if (!"multipolygon".equals(v)) { + currRelation = null; + } + break; + default: + break; + } + } + } + break; + + case XMLStreamConstants.CHARACTERS: +// tagContent = reader.getText().trim(); + break; + + case XMLStreamConstants.END_ELEMENT: + switch (reader.getLocalName()) { + case "relation": + if (currRelation != null) { + if (currRelation.outerWays.size() > 0) { + if ("lasy".equals(genGoal)) { + if (((currRelation.landuse != null) && currRelation.landuse == EOSMLanduse.FOREST) + || ((currRelation.natural != null) && currRelation.natural == EOSMNatural.WOOD)) { + + relationsMap.put(currRelation.id, currRelation); + for (Way way : currRelation.outerWays) { + // kopiuje tagi dla lamanych zewnetrznych + way.copyTags(currRelation); + relationWaysMap.put(way.id, way); + } + for (Way way : currRelation.innerWays) { + relationWaysMap.put(way.id, way); + // nie kopiuje tagow dla lamanych wewnetrznych, gdyz one maja wlasne tagi + } + } + } else if ("wody".equals(genGoal)) { + if (((currRelation.natural != null) && currRelation.natural == EOSMNatural.WATER) + || ((currRelation.waterway != null) && currRelation.waterway == EOSMWaterway.RIVERBANK)) { + + relationsMap.put(currRelation.id, currRelation); + for (Way way : currRelation.outerWays) { + // kopiuje tagi dla lamanych zewnetrznych + way.copyTags(currRelation); + relationWaysMap.put(way.id, way); + } + for (Way way : currRelation.innerWays) { + relationWaysMap.put(way.id, way); + // nie kopiuje tagow dla lamanych wewnetrznych, gdyz one maja wlasne tagi + } + } + } + } + } + currRelation = null; + break; + default: + break; + } + break; + + default: + break; + } + } + reader.close(); + } + + static void generujDaneTerenowe(String genGoal, HashMap relationsMap, + HashMap waysMap, HashMap buildingNodesMap) { + int count = 0; + int m1 = relationsMap.size() / 100; + m1 = (m1 == 0) ? 1 : m1; + int m2 = waysMap.size() / 100; + m2 = (m2 == 0) ? 1 : m2; + ArrayList wayList = new ArrayList<>(waysMap.values()); + // określenie formatu odczytu danych terenowych przed uaktualnieniem + if ("lasy".equals(genGoal)) { + for (Relation rel : relationsMap.values()) { + count++; + rel.removeLastNodes(); + rel.removeSimilarNodes(); + rel.removeCollinearNodes(true); + rel.writeAreaFeatureIntoSquares(EAreaFeature.FOREST); + if (count % m1 == 0) { + LOGGER.debug(">><<>><<>><<: rel count= " + count + ", " + genGoal); + } + } + count = 0; + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.FOREST, false); + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("wody".equals(genGoal)) { + for (Relation rel : relationsMap.values()) { + count++; + rel.removeLastNodes(); + rel.removeSimilarNodes(); + rel.removeCollinearNodes(true); + rel.writeAreaFeatureIntoSquares(EAreaFeature.WATER); + if (count % m1 == 0) { + LOGGER.debug(">><<>><<>><<: rel count= " + count + ", " + genGoal); + } + } + count = 0; + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.WATER, false); + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("bagna".equals(genGoal)) { + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.SWAMP, false); + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("zabudowa".equals(genGoal)) { + // wygenerowanie danych o stopniu zabudowy na podstawie danych adresowych +// HashMap newNodesMap = new HashMap<>(); +// for (Node node : buildingNodesMap.values()) { +// Node node1 = newNodesMap.get(node); +// if (node1 != null) { +// node1.buildingsCount += node.buildingsCount; +// } else { +// newNodesMap.put(node, node); +// } +// } +// buildingNodesMap.clear(); + + for (Node node : buildingNodesMap.values()) { + node.writeAreaFeatureIntoSquare(EAreaFeature.BUILDINGS); + } + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } +// for (int i = 0; i < way.nodes.size(); i++) { +// Node node = way.nodes.get(i); +// if (node.idX <= 0 || node.idY <= 0) { +// logger.debug("!!!! niepoprawny wezel id= " + node.id + ", lamana id= " + way.id); +// } +// } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.BUILDINGS, false); + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("drogi".equals(genGoal)) { + for (Way way : wayList) { + count++; +// if ("249908111".equals(way.id) || "238353558".equals(way.id)) { +// logger.debug("!!!!!"); +// } + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.ROAD); + } +// if (count % 10000 == 0) { +// logger.debug("-*-*-*-*-"); +// logger.debug("maxMemory=" + java.lang.Runtime.getRuntime().maxMemory() +// + ", totalMemory=" + java.lang.Runtime.getRuntime().totalMemory() +// + ", freeMemory=" + java.lang.Runtime.getRuntime().freeMemory()); +// } +// count++; + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("rzeki".equals(genGoal)) { + for (Way way : wayList) { + count++; + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.WATER_WAY); + } + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("rowy".equals(genGoal)) { + for (Way way : wayList) { + count++; + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.DITCH); + } + if (count % m2 == 0) { + LOGGER.debug("<><><><><><>: way count= " + count + ", " + genGoal); + } + } + } + } + + static void generujDaneTerenowe2(String genGoal, HashMap relationsMap, + HashMap waysMap, HashMap buildingNodesMap) { + int count = 0; + int m1 = relationsMap.size() / 100; + m1 = (m1 == 0) ? 1 : m1; + ArrayList wayList = new ArrayList<>(waysMap.values()); + // określenie formatu odczytu danych terenowych przed uaktualnieniem + if ("lasy".equals(genGoal)) { + for (Relation rel : relationsMap.values()) { + count++; + rel.removeLastNodes(); + rel.removeSimilarNodes(); + rel.removeCollinearNodes(true); + rel.writeAreaFeatureIntoSquares(EAreaFeature.FOREST); + if (count % m1 == 0) { + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " >><<>><<>><<: rel count= " + count + ", " + genGoal); + } + } + } + count = 0; + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.FOREST, false); + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("wody".equals(genGoal)) { + for (Relation rel : relationsMap.values()) { + count++; + rel.removeLastNodes(); + rel.removeSimilarNodes(); + rel.removeCollinearNodes(true); + rel.writeAreaFeatureIntoSquares(EAreaFeature.WATER); + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " >><<>><<>><<: rel count= " + count + ", " + genGoal); + } + } + count = 0; + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.WATER, false); + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("bagna".equals(genGoal)) { + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.SWAMP, false); + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("zabudowa".equals(genGoal)) { + // wygenerowanie danych o stopniu zabudowy na podstawie danych adresowych +// HashMap newNodesMap = new HashMap<>(); +// for (Node node : buildingNodesMap.values()) { +// Node node1 = newNodesMap.get(node); +// if (node1 != null) { +// node1.buildingsCount += node.buildingsCount; +// } else { +// newNodesMap.put(node, node); +// } +// } +// buildingNodesMap.clear(); + + for (Node node : buildingNodesMap.values()) { + node.writeAreaFeatureIntoSquare(EAreaFeature.BUILDINGS); + } + for (Way way : wayList) { + count++; + // usunięcie powtórzenia węzła z łamanej definiującej obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } +// for (int i = 0; i < way.nodes.size(); i++) { +// Node node = way.nodes.get(i); +// if (node.idX <= 0 || node.idY <= 0) { +// logger.debug("!!!! niepoprawny wezel id= " + node.id + ", lamana id= " + way.id); +// } +// } + way.removeSimilarNodes(); + way.removeCollinearNodes(true); + way.writeAreaFeatureIntoSquares(EAreaFeature.BUILDINGS, false); + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("drogi".equals(genGoal)) { + for (Way way : wayList) { + count++; +// if ("249908111".equals(way.id) || "238353558".equals(way.id)) { +// logger.debug("!!!!!"); +// } + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.ROAD); + } +// if (count % 10000 == 0) { +// logger.debug("-*-*-*-*-"); +// logger.debug("maxMemory=" + java.lang.Runtime.getRuntime().maxMemory() +// + ", totalMemory=" + java.lang.Runtime.getRuntime().totalMemory() +// + ", freeMemory=" + java.lang.Runtime.getRuntime().freeMemory()); +// } +// count++; + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("rzeki".equals(genGoal)) { + for (Way way : wayList) { + count++; + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.WATER_WAY); + } + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } else if ("rowy".equals(genGoal)) { + for (Way way : wayList) { + count++; + way.removeSimilarNodes(); + way.removeCollinearNodes(false); + way.removeSteps(); + if (way.nodes.size() > 1) { + way.writeLinearFeatureIntoSquares(ELinearFeature.DITCH); + } + synchronized (synch) { + LOGGER.debug(Thread.currentThread().getName() + " <><><><><><>: way count= " + count + ", " + genGoal); + } + } + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/PUWGCoord.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/PUWGCoord.java new file mode 100644 index 0000000..34e2e5d --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/PUWGCoord.java @@ -0,0 +1,35 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +/** + * Współrzędne punktu odwzorowania kartograficznego PUWG 1992 lub 2000. + *

+ * Wartośći współrzędnych [metry]. + * + */ +public class PUWGCoord { + /** + * Współrzędna X (oś odcietych) odwzorowania kartograficznego [metry]. + */ + public double easting; + /** + * Współrzędna Y (oś rzędnych) odwzorowania kartograficznego [metry]. + */ + public double northing; + /** + * proj = 1 odpowiada odwzorowaniu 1992, natomiast każda inna odwzorowaniu 2000 + */ + public int proj; + + public PUWGCoord() { + this.easting = 0; + this.northing = 0; + // PUWG 1992 + this.proj = 1; + } + + public PUWGCoord(double easting, double northing, int proj) { + this.easting = easting; + this.northing = northing; + this.proj = proj; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Relation.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Relation.java new file mode 100644 index 0000000..6c9b4c5 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Relation.java @@ -0,0 +1,204 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; + +/** + * Bazowa klasa dla obiektów OpenStreetMap. + */ +public class Relation { + private static final Logger logger = Logger.getLogger(Relation.class); + + public String id; + /** + * Tag oznacza obiekt obszarowy.

+ * k="natural" - v="water" | v="reservoir" | v="wetland" | v="wood" + */ + public EOSMNatural natural; + + /** + * Tag ten oznacza rzekę lub ciek wodny opisywany liniowo. + *

Wyjątek: waterway=riverbank, który opisuje obszar. + *

k="waterway" - v="river" | v="riverbank" | v="stream" | v="canal" | v="drain" | v="ditch" + */ + public EOSMWaterway waterway; + + /** + * Tag stosowany w połączeniu z tagiem obszarowym: natural=water.

+ * k="water" - v="lake" | v="reservoir" | v="river" | v="canal" | v="cove" | v="lagoon" | v="pond" + */ + public EOSMWater water; + + /** + * Tag oznacza obiekt obszarowy.

+ * k="landuse" - v="forest" | v="residential" | v="commercial" + */ + public EOSMLanduse landuse; + + /** + * Tag oznacza obiekt obszarowy.

+ * k="landcover" - v="trees" | v="water" | v="grass" + */ + public EOSMLandcover landcover; + + ArrayList outerWays = new ArrayList(); + ArrayList innerWays = new ArrayList(); + + public Relation(String id) { + this.id = id; + } + + public Relation(Relation oryg) { + this.id = oryg.id; + } + + public void copyTags(Relation oryg) { + this.id = oryg.id; + this.natural = oryg.natural; + this.waterway = oryg.waterway; + this.water = oryg.water; + this.landuse = oryg.landuse; + this.landcover = oryg.landcover; + } + + ArrayList generatePolygons(ArrayList wayList) { + ArrayList polygons = new ArrayList(); + Way newPolygon = new Way((String)null); + int prevLast = -1; + for (int iWay = 0; iWay < wayList.size(); iWay++) { + Way testWay = wayList.get(iWay); + if (testWay.nodes.size() < 2) { + continue; + } + int currLast = testWay.nodes.size() - 1; + Node currFirstNode = testWay.nodes.get(0); + Node currLastNode = testWay.nodes.get(currLast); + if (prevLast < 0) { + // biezacy wielokat jeszcze nie zawiera wezlow +// newPolygon = new Way((String)null); + newPolygon.nodes = testWay.nodes; + newPolygon.copyTags(testWay); + if (currFirstNode == currLastNode) { + // lamana jest wielokatem + polygons.add(newPolygon); + prevLast = -1; + newPolygon = new Way((String)null); + } else { + prevLast = newPolygon.nodes.size() - 1; + } + } else { + if (currFirstNode == currLastNode) { + // !! Blad w danych - brak ciaglosci lamanych: poprzedni wielokat sie nie zamknal + // lamana jest wielokatem, +// newPolygon = new Way((String)null); + newPolygon.nodes = testWay.nodes; + newPolygon.copyTags(testWay); + polygons.add(newPolygon); + prevLast = -1; + newPolygon = new Way((String)null); + } else { + // biezacy wielokat zawiera juz wezly + Node prevFirstNode = newPolygon.nodes.get(0); + Node prevLastNode = newPolygon.nodes.get(prevLast); + if (currFirstNode == prevLastNode) { + for (int jNode = 1; jNode < testWay.nodes.size(); jNode++) { + newPolygon.nodes.add(testWay.nodes.get(jNode)); + } + if (currLastNode == prevFirstNode) { + // lamana zamyka wielokat + polygons.add(newPolygon); + prevLast = -1; + newPolygon = new Way((String)null); + } else { + // lamana nie zamyka jeszcze wielokata + prevLast = newPolygon.nodes.size() - 1; + } + } else if (currLastNode == prevFirstNode) { + // odwracam kolejnosc dodawanych wezlow z biezacej lamanej + for (int jNode = 1; jNode < newPolygon.nodes.size(); jNode++) { + testWay.nodes.add(newPolygon.nodes.get(jNode)); + } + newPolygon.nodes = testWay.nodes; +// for (int jNode = currLast - 1; jNode >= 0; jNode--) { +// newPolygon.nodes.add(testWay.nodes.get(jNode)); +// } + if (currFirstNode == prevLastNode) { + // lamana zamyka wielokat + polygons.add(newPolygon); + prevLast = -1; + newPolygon = new Way((String)null); + } else { + // lamana nie zamyka jeszcze wielokata + prevLast = newPolygon.nodes.size() - 1; + } + } else { + // !! Blad w danych - brak ciaglosci lamanych: poprzedni wielokat sie nie zamknal + // lamana nowego obszaru +// newPolygon = new Way((String)null); + newPolygon.nodes = testWay.nodes; + newPolygon.copyTags(testWay); + prevLast = newPolygon.nodes.size() - 1; + } + } + } + } + return polygons; + } + + void writeAreaFeatureIntoSquares(EAreaFeature type) { + for (Way way : outerWays) { + // wpisanie w kwadraty danej cechy dla wielokatow opisujacych zewnetrzna powloke obszaru + way.writeAreaFeatureIntoSquares(type, false); + } + for (Way way : innerWays) { + // wykreslenie z kwadratow danej cechy dla wielokatow opisujacych wewnetrzne podobszary + way.writeAreaFeatureIntoSquares(type, true); + } + } + + void removeSimilarNodes() { + for (Way way : outerWays) { + // wpisanie w kwadraty danej cechy dla wielokatow opisujacych zewnetrzna powloke obszaru + way.removeSimilarNodes(); + } + for (Way way : innerWays) { + // wykreslenie z kwadratow danej cechy dla wielokatow opisujacych wewnetrzne podobszary + way.removeSimilarNodes(); + } + } + + void removeCollinearNodes(boolean isPolygon) { + for (Way way : outerWays) { + // wpisanie w kwadraty danej cechy dla wielokatow opisujacych zewnetrzna powloke obszaru + way.removeCollinearNodes(isPolygon); + } + for (Way way : innerWays) { + // wykreslenie z kwadratow danej cechy dla wielokatow opisujacych wewnetrzne podobszary + way.removeCollinearNodes(isPolygon); + } + } + + void removeLastNodes() { + for (Way way : outerWays) { + // usunięcie powtórzenia węzła z wielokata definiującego obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + } + for (Way way : innerWays) { + // usunięcie powtórzenia węzła z wielokata definiującego obszar + if (way.nodes.size() > 1) { + way.nodes.remove(way.nodes.size() - 1); + } + } + } + + @Override + public String toString() { + return "Relation{id=" + id + + ", outerWays.len=" + outerWays.size() + + ", innerWays.len=" + innerWays.size() + + '}'; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/UTMCoord.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/UTMCoord.java new file mode 100644 index 0000000..54e246c --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/UTMCoord.java @@ -0,0 +1,34 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +/** + * Współrzędna w układzie UTM.

+ * Przykład: 17T 630084 4833438, + * gdzie: xZone = 17, yZone = T, easting = 630084, northing = 4833438 + *

+ * xZone - nr strefy UTM wg. podziału południkowego (zwracane numery od 1 do 60, każda strefa ma sześć stopni); + *

+ * yZone - nr strefy wg. podziału równoleżnikowego (zwracane wartości: CDEFGHJKLMNPQRSTUVWX). + *

+ * easting - odległość w kierunku wschodnim od początku układu współrzędnych w danej strefie UTM [metr]. + *

+ * northing - odległość w kierunku północnym od początku układu współrzędnych w danej strefie UTM [metr]. + */ +public class UTMCoord { + + /** + * Nr strefy UTM wg. podziału południkowego (zwracane numery od 1 do 60, każda strefa ma sześć stopni). + */ + public int xZone; + /** + * Nr strefy wg. podziału równoleżnikowego (zwracane wartości: CDEFGHJKLMNPQRSTUVWX). + */ + public char yZone; + /** + * Odległość w kierunku wschodnim od początku układu współrzędnych w danej strefie UTM [metr]. + */ + public double easting; + /** + * Odległość w kierunku północnym od początku układu współrzędnych w danej strefie UTM [metr]. + */ + public double northing; +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Way.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Way.java new file mode 100644 index 0000000..669d48c --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Way.java @@ -0,0 +1,603 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.common.EGeoDirection; +import pl.wat.ms4ds.terenfunkcje.*; + +import java.util.ArrayList; + +/** + * Bazowa klasa dla obiektów OpenStreetMap. + */ +public class Way { + private static final Logger logger = Logger.getLogger(Way.class); + + public String id; + + ArrayList nodes = new ArrayList(); + + /** + * k="highway" - v="motorway" | v="trunk" | v="primary" | v="secondary" | v="tertiary" | v="unclassified" + * | v="residential" | v="pedestrian" | v="track" + * | v="motorway_link" | v="trunk_link" | v="primary_link" | v="secondary_link" | v="tertiary_link" + */ + public EOSMHighway highway; + + /** + * Tag oznacza obiekt obszarowy.

+ * k="natural" - v="water" | v="reservoir" | v="wetland" | v="wood" + */ + public EOSMNatural natural; + + /** + * Tag ten oznacza rzekę lub ciek wodny opisywany liniowo. + *

Wyjątek: waterway=riverbank, który opisuje obszar. + *

k="waterway" - v="river" | v="riverbank" | v="stream" | v="canal" | v="drain" | v="ditch" + */ + public EOSMWaterway waterway; + + /** + * Tag stosowany w połączeniu z tagiem obszarowym: natural=water.

+ * k="water" - v="lake" | v="reservoir" | v="river" | v="canal" | v="cove" | v="lagoon" | v="pond" + */ + public EOSMWater water; + + /** + * Tag oznacza obiekt obszarowy.

+ * k="landuse" - v="forest" | v="residential" | v="commercial" + */ + public EOSMLanduse landuse; + + /** + * k="amenity" - v="hospital" | v="school" | v="post_office" | v="police" | v="marketplace" | v="fire_station" | + * v="theatre" | v="cinema" | v="pharmacy" | v="nursing_home" | v="bank" | v="fuel" + * v="university" | v="library" + */ + public EOSMAmenity amenity; + + /** + * k="building" - v="yes" | v="chapel" | v="church" | v="house" | v="office" | v="manufacture" | v="industrial" + */ + public EOSMBuilding building; + + /** + * k="bridge" - v="yes" | "viaduct" + */ + public EOSMBridge bridge; + + /** + * Tag oznacza obiekt obszarowy.

+ * k="landcover" - v="trees" | v="water" | v="grass" + */ + public EOSMLandcover landcover; + + public Way(String id) { + this.id = id; + } + + public Way(Way oryg) { + this.id = oryg.id; + } + + public void copyTags(Way oryg) { + this.highway = oryg.highway; + this.natural = oryg.natural; + this.waterway = oryg.waterway; + this.water = oryg.water; + this.landuse = oryg.landuse; + this.amenity = oryg.amenity; + this.building = oryg.building; + this.bridge = oryg.bridge; + this.landcover = oryg.landcover; + } + + public void copyTags(Relation oryg) { + this.natural = oryg.natural; + this.waterway = oryg.waterway; + this.water = oryg.water; + this.landuse = oryg.landuse; + this.landcover = oryg.landcover; + } + + public boolean tagged() { + if (landuse != null || natural != null || landcover != null + || highway != null || waterway != null + || building != null || amenity != null || bridge != null) { + return true; + } + return false; + } + + void removeSimilarNodes() { + if (nodes.size() == 0) { + return; + } + Node node_i; + Node node_j; + Node similarToNode[] = new Node[nodes.size()]; + boolean similarFound = false; + for (int i = 0; i < nodes.size() - 1; i++) { + node_i = nodes.get(i); + if (similarToNode[i] != null) { + continue; + } + for (int j = i + 1; j < nodes.size(); j++) { + node_j = nodes.get(j); + if (node_i.idX == node_j.idX && node_i.idY == node_j.idY) { + similarToNode[j] = node_i; + node_i.buildingsCount += node_j.buildingsCount; + similarFound = true; + } + } + } + if (similarFound) { + ArrayList newList = new ArrayList<>(); + for (int i = 0; i < nodes.size(); i++) { + node_i = nodes.get(i); + if (similarToNode[i] == null) { + newList.add(node_i); + } + } + nodes = newList; + removeSimilarNodes(); + } + } + + void removeCollinearNodes(boolean isPolygon) { + if (nodes.size() < 3) { + return; + } + boolean toDelete[] = new boolean[nodes.size()]; + Node node_i; + Node node_i_next; + Node node_i_prev; + boolean collinearFound = false; + int dlMax = nodes.size(); + int iStart = 0; + if (!isPolygon) { + // łamana otwarta + dlMax = 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)) { + // i-ty do usuniecia + toDelete[i] = 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)) { + // i-1-ty do usuniecia + toDelete[i_minus_1] = 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)) { + // i+1-ty do usuniecia + toDelete[i_plus_1] = true; + collinearFound = true; + } + } + if (collinearFound) { + ArrayList newList = new ArrayList(); + for (int i = 0; i < nodes.size(); i++) { + node_i = nodes.get(i); + if (!toDelete[i]) { + newList.add(node_i); + } + } +// logger.trace("Liczba oryg. wezlow= {}, liczba niewspolliniowych wezlow= {}, roznica= {}", nodes.size(), newList.size(), nodes.size() - newList.size()); + nodes = newList; + removeCollinearNodes(isPolygon); + } + } + + void removeSteps() { + if (nodes.size() < 3) { + return; + } + boolean toDelete[] = new boolean[nodes.size()]; + Node node_i; + Node node_j; + Node node_k; + 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) { + // wezly moga tworzyc schodek + if (absX_i_j + absX_j_k == 1) { + // wezly tworza schodek, zatem srodkowy wezel schodka do usuniecia + toDelete[i + 1] = true; + bylSchodek = true; + // przeskok o usuwany wezel + i++; + } + } + } + if (bylSchodek) { + ArrayList newList = new ArrayList(); + for (int i = 0; i < nodes.size(); i++) { + if (!toDelete[i]) { + newList.add(nodes.get(i)); + } + } + if (nodes.size() > newList.size()) { +// logger.trace("Liczba oryg. wezlow= {}, liczba roznych wezlow= {}, roznica= {}", nodes.size(), newList.size(), nodes.size() - newList.size()); + } + nodes = newList; + removeSteps(); + } + } + + void writeLinearFeatureIntoSquares(ELinearFeature type) { + if (nodes.size() == 0) { + return; + } + GridCoord[] punktyLamanej = new GridCoord[nodes.size()]; + for (int i = 0; i < nodes.size(); i++) { + punktyLamanej[i] = new GridCoord(nodes.get(i).idX, nodes.get(i).idY); + } + Kwadrat kw0; + Kwadrat kw1; + GridCoord id0; + GridCoord id1; + EGeoDirection kier; + GridCoord[] kwadraty = GeomUtils.kwadratyLamanej2(punktyLamanej); +// float dlug = GeomUtils.dlugoscDrogiPoKwadratch(kwadraty); + for (int i = 0; i < kwadraty.length - 1; i++) { + try { + id0 = kwadraty[i]; + kw0 = Teren.getKwadrat(id0.x, id0.y); + id1 = kwadraty[i + 1]; + kw1 = Teren.getKwadrat(id1.x, id1.y); + kier = GeomUtils.kierunekDlaSasiada(id0, id1); + switch (type) { + case ROAD: + kw0.setJestDroga(kier, true); + kw1.setJestDroga(kier.oppositeDirect(), true); + break; + case WATER_WAY: + kw0.setJestPrzeszkodaWodna(kier, true); + kw1.setJestPrzeszkodaWodna(kier.oppositeDirect(), true); + break; + case DITCH: + kw0.setJestRow(kier, true); + kw1.setJestRow(kier.oppositeDirect(), true); + break; + default: + } + } catch (Exception e) { + logger.warn(e.toString()); + } + } + } + + public static void writeAreaFeatureIntoSquares2(EAreaFeature type, boolean clearFeature, GridCoord[] polygon) { + if (polygon.length > 20) { + // podział wielokata na dwa mniejsze + int m = 2; + GridCoord pocz = polygon[0]; + boolean poprawnyPodzial = false; + while (m < polygon.length - 1 && !poprawnyPodzial) { + // sprawdzenie, czy punkty wielokata po podziale sa po jednej stronie wektora podzialu + poprawnyPodzial = true; + GridCoord kon = polygon[m]; + for (int i = 0; i < polygon.length; i++) { + int i_puls_1 = (i + 1) % polygon.length; + boolean przeciecie = GeomUtils.intersection(pocz, kon, polygon[i], polygon[i_puls_1]); + if (przeciecie) { + // sprawdzenie, czy jakiś koniec jednego odcinka jest równy końcowi drugiego odcinka + boolean b = pocz.rowne(polygon[i].x, polygon[i].y) || + pocz.rowne(polygon[i_puls_1].x, polygon[i_puls_1].y) || + kon.rowne(polygon[i].x, polygon[i].y) || + kon.rowne(polygon[i_puls_1].x, polygon[i_puls_1].y); + if (!b) { + poprawnyPodzial = false; + m++; + break; + } + } + } + } + if (poprawnyPodzial) { + // punkt podziału wielokąta jest poprawny, zatem dzielę wielokąt na dwa + GridCoord[] polygon1 = new GridCoord[m + 1]; + for (int i = 0; i < polygon1.length; i++) { + polygon1[i] = polygon[i]; + } + writeAreaFeatureIntoSquares2(type, clearFeature, polygon1); + + GridCoord[] polygon2 = new GridCoord[polygon.length - m + 1]; + polygon2[0] = polygon[0]; + for (int i = m; i < polygon.length; i++) { + polygon2[i - m + 1] = polygon[i]; + } + writeAreaFeatureIntoSquares2(type, clearFeature, polygon2); + } else { + // nie udało się poprawnie podzielić wielokąta, zatem przesuwam wierzchołki, aby zmienić wierzchołek + // startowy, który jest wierchołkiem referencyjnym podziału (drugi wierzchołek podziału jest szukany) + GridCoord temp = polygon[0]; + for (int i = 0; i < polygon.length - 1; i++) { + polygon[i] = polygon[i + 1]; + } + polygon[polygon.length - 1] = temp; + writeAreaFeatureIntoSquares2(type, clearFeature, polygon); + } + return; + } + float val = (clearFeature) ? 0.0f : 1.0f; + int minX = polygon[0].x; + int minY = polygon[0].y; + int maxX = polygon[0].x; + int maxY = polygon[0].y; + for (int i = 1; i < polygon.length; i++) { + minX = (polygon[i].x < minX) ? polygon[i].x : minX; + minY = (polygon[i].y < minY) ? polygon[i].y : minY; + maxX = (polygon[i].x > maxX) ? polygon[i].x : maxX; + maxY = (polygon[i].y > maxY) ? polygon[i].y : maxY; + } + GridCoord idTest = new GridCoord(); + boolean inside; + for (int j = maxY; j >= minY; j--) { + for (int i = minX; i <= maxX; i++) { + idTest.x = i; + idTest.y = j; + Kwadrat kw = Teren.getKwadrat(idTest.x, idTest.y); + if (kw == Kwadrat.EMPTY_SQUARE) { + continue; + } + inside = GeomUtils.insidePolygon(polygon, idTest); + if (inside) { + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + } + } + } + } + + public static void writeAreaFeatureIntoSquares(EAreaFeature type, boolean clearFeature, GridCoord[] polygon, + int minX,int maxX, int minY, int maxY) { + float val = (clearFeature) ? 0.0f : 1.0f; + GridCoord idTest = new GridCoord(); + boolean inside; + for (int i = minX; i <= maxX; i++) { + for (int j = minY; j <= maxY; j++) { + idTest.x = i; + idTest.y = j; + Kwadrat kw = Teren.getKwadrat(idTest.x, idTest.y); + if (kw == Kwadrat.EMPTY_SQUARE) { + continue; + } + inside = GeomUtils.insidePolygon(polygon, idTest); + if (inside) { + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + } + } + } + } + + void writeAreaFeatureIntoSquares2(EAreaFeature type, boolean clearFeature) { + if (nodes.size() == 0) { + return; + } + Kwadrat kw; + float val = (clearFeature) ? 0.0f : 1.0f; + if (nodes.size() == 1) { + kw = Teren.getKwadrat(nodes.get(0).idX, nodes.get(0).idY); + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + return; + } + if (nodes.size() == 2) { + GridCoord[] kwadraty = GeomUtils.kwadratyOdcinka(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.getKwadrat(kwadraty[i].x, kwadraty[i].y); + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + } + return; + } + GridCoord[] wielokat = new GridCoord[nodes.size()]; + for (int i = 0; i < nodes.size(); i++) { + wielokat[i] = new GridCoord(nodes.get(i).idX, nodes.get(i).idY); + } + writeAreaFeatureIntoSquares2(type, clearFeature, wielokat); + } + + void writeAreaFeatureIntoSquares(EAreaFeature type, boolean clearFeature) { + if (nodes.size() == 0) { + return; + } + Kwadrat kw; + float val = (clearFeature) ? 0.0f : 1.0f; + if (nodes.size() == 1) { + kw = Teren.getKwadrat(nodes.get(0).idX, nodes.get(0).idY); + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + return; + } + if (nodes.size() == 2) { + GridCoord[] kwadraty = GeomUtils.kwadratyOdcinka(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.getKwadrat(kwadraty[i].x, kwadraty[i].y); + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } + } + return; + } + GridCoord[] wielokat = new GridCoord[nodes.size()]; + int minX = nodes.get(0).idX; + int minY = nodes.get(0).idY; + int maxX = nodes.get(0).idX; + int maxY = nodes.get(0).idY; + for (int i = 0; i < nodes.size(); i++) { + wielokat[i] = new GridCoord(nodes.get(i).idX, nodes.get(i).idY); + minX = (wielokat[i].x < minX) ? wielokat[i].x : minX; + minY = (wielokat[i].y < minY) ? wielokat[i].y : minY; + maxX = (wielokat[i].x > maxX) ? wielokat[i].x : maxX; + maxY = (wielokat[i].y > maxY) ? wielokat[i].y : maxY; + } + int ileKwTest = (maxX - minX) * (maxY - minY); + if (ileKwTest > 100000) { + int dx = maxX - minX; + int dy = maxY - minY; + Worker[] workers = new Worker[4]; + workers[0] = new Worker(type, clearFeature, wielokat, minX, minX + dx / 2, minY, minY + dy / 2); + workers[1] = new Worker(type, clearFeature, wielokat, minX + dx / 2 + 1, maxX, minY, minY + dy / 2); + workers[2] = new Worker(type, clearFeature, wielokat, minX, minX + dx / 2, minY + dy / 2 + 1, maxY); + workers[3] = new Worker(type, clearFeature, wielokat, minX + dx / 2 + 1, maxX, minY + dy / 2 + 1, maxY); + for (int i = 0; i < workers.length; i++) { + workers[i].start(); + } + return; + } + GridCoord idTest = new GridCoord(); + 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; +// char c = ' '; +// liczKw++; + kw = Teren.getKwadrat(idTest.x, idTest.y); + if (kw == Kwadrat.EMPTY_SQUARE) { + continue; + } + nalezyDoWielokata = GeomUtils.insidePolygon(wielokat, idTest); + if (nalezyDoWielokata) { +// c = 'O'; + switch (type) { + case FOREST: + kw.setStopienZalesienia(val); + break; + case WATER: + kw.setStopienZawodnienia(val); + break; + case SWAMP: + kw.setStopienZabagnienia(val); + break; + case BUILDINGS: + kw.setStopienZabudowy(val); + break; + default: + } +// liczKwObszaru++; + } +// rysowanie wielokata +// for (int k = 0; k < wielokat.length; k++) { +// if (wielokat[k].equals(idTest)) { +// c = 'X'; +// break; +// } +// } +// System.out.print(c); +// System.out.print(' '); + } +// System.out.println(); + } + } + + @Override + public String toString() { + return "Way{id=" + id + + ", len=" + nodes.size() + + ", nodes=" + nodes + + '}'; + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Worker.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Worker.java new file mode 100644 index 0000000..580c9fe --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/konwersja/Worker.java @@ -0,0 +1,54 @@ +package pl.wat.ms4ds.terenfunkcje.konwersja; + +import org.apache.log4j.Logger; +import pl.wat.ms4ds.terenfunkcje.GridCoord; + +import java.util.ArrayList; + +/** + * + */ +public class Worker extends Thread { + + private static final Logger logger = Logger.getLogger(Worker.class); + + static ArrayList workers = new ArrayList<>(); + + static Integer nr = 0; + + EAreaFeature type; + boolean clearFeature; + GridCoord[] polygon; + int minX; + int maxX; + int minY; + int maxY; + + public Worker(EAreaFeature type, boolean clearFeature, GridCoord[] polygon, int minX, int maxX, int minY, int maxY) { + super("Worker_" + nr++); + this.type = type; + this.clearFeature = clearFeature; + this.polygon = polygon; + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + workers.add(this); + } + + @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)); + } + 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)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTDataProvider.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTDataProvider.java new file mode 100644 index 0000000..46dff36 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTDataProvider.java @@ -0,0 +1,215 @@ +package pl.wat.ms4ds.terenfunkcje.nmt; + +import org.apache.log4j.Logger; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamReader; +import java.io.*; +import java.net.URI; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.util.HashMap; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class NMTDataProvider { + private static final Logger LOGGER = Logger.getLogger(NMTDataProvider.class); + + public static void main(String[] args) throws Exception { +// File dir = new File(System.getProperty("user.home") + "/nmt/gugik_SkorowidzNMT2018.gml"); +// HashMap map = new HashMap<>(); +// String fn0 = "D:/nmt/gugik_SkorowidzNMT20"; +// for (int i = 18; i < 26; i++) { +// readFileLinksFromGUGiKxml(fn0 + i + ".gml", map); +// } +// saveLinks("D:/nmt/gugik_links.txt", map); + + // start = 580-730, start=1091-1230 + // +// String dir = "C:/Workspace/nmt/gugik_1m/"; +// String links_fn = "C:/Workspace/nmt/gugik_links.txt"; + String dir = args[0]; + String links_fn = args[1]; +// int start = Integer.parseInt(args[2]); +// int end = Integer.parseInt(args[3]); + + Set files = listFiles("C:/Workspace/nmt/gugik_1m/"); + +// downloadFileSet(links_fn, start, end, dir); + +// ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); +// executor.execute(() -> { +// try { +// downloadFileSet(links_fn, 290, 730, dir); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// }); +// executor.execute(() -> { +// try { +// downloadFileSet(links_fn, 779, 1230, dir); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// }); +// executor.shutdown(); +// executor.awaitTermination(25, TimeUnit.MINUTES); + LOGGER.info("Koniec"); + } + + private static void saveLinks(String fn, HashMap map) throws IOException { + BufferedWriter writer = new BufferedWriter(new FileWriter(fn)); + for (String val : map.values()) { + writer.write(val); + writer.newLine(); + } + writer.close(); + } + + private static void downloadFile2(String urlStr, String dirName) throws IOException { + String fn = urlStr.substring(urlStr.lastIndexOf('_') + 1); + String path = dirName + fn; +// URI uri = URI.create("https://opendata.geoportal.gov.pl/NumDaneWys/NMT/81441/81441_1643337_N-34-125-D-d-3-1.asc"); + URI uri = URI.create(urlStr); + InputStream inputStream = uri.toURL().openStream(); + ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream); + FileOutputStream fos = new FileOutputStream(path); + FileChannel fc = fos.getChannel(); + fc.transferFrom(readableByteChannel, 0, Long.MAX_VALUE); + fc.close(); + fos.close(); + } + + public static Set listFiles(String dir) { + return Stream.of(new File(dir).listFiles()) + .filter(file -> !file.isDirectory()) + .map(File::getName) + .collect(Collectors.toSet()); + } + + private static void downloadFile(String urlStr, String dirName) throws IOException { + String fn = urlStr.substring(urlStr.lastIndexOf('_') + 1); + String path = dirName + fn; + URI uri = URI.create(urlStr); + try (InputStream inputStream = uri.toURL().openStream();) { + BufferedInputStream bis = new BufferedInputStream(inputStream); + FileOutputStream fos; + String fext = fn.substring(fn.lastIndexOf('.') + 1); + byte[] buffer = new byte[1024]; + int count; + if (!fext.equals("zip")) { + fos = new FileOutputStream(path + ".zip"); + ZipOutputStream zipOut = new ZipOutputStream(fos); + File fileToZip = new File(path); + ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); + zipOut.putNextEntry(zipEntry); + while ((count = bis.read(buffer)) != -1) { + zipOut.write(buffer, 0, count); + } + zipOut.close(); + bis.close(); + return; + } + fos = new FileOutputStream(path); + while ((count = bis.read(buffer, 0, 1024)) != -1) { + fos.write(buffer, 0, count); + } + fos.close(); + bis.close(); + } + } + + private static void zipFile(String sourceFile) throws IOException { + FileOutputStream fos = new FileOutputStream(sourceFile + ".zip"); + ZipOutputStream zipOut = new ZipOutputStream(fos); + + File fileToZip = new File(sourceFile); + FileInputStream fis = new FileInputStream(fileToZip); + ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); + zipOut.putNextEntry(zipEntry); + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zipOut.write(bytes, 0, length); + } + zipOut.close(); + fis.close(); + fos.close(); + } + + private static void downloadFileSet(String fn, int start, int end, String dirName) throws IOException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fn)))) { + File dir = new File(dirName); + dir.mkdirs(); + String line; + int i = 0; + while ((line = br.readLine()) != null) { + if (i >= start) { + if (i >= end) { + break; + } +// LOGGER.debug("Start i={}, addr={}", i, line); + try { + downloadFile(line, dirName); + } catch (IOException e) { + LOGGER.debug("Downloading error: " + e.getMessage()); + } + } + i++; + } + } + } + + // 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 { + XMLInputFactory factory = XMLInputFactory.newInstance(); + FileInputStream is = new FileInputStream(fn); + BufferedInputStream bis = new BufferedInputStream(is); +// XMLStreamReader reader = factory.createXMLStreamReader(ClassLoader.getSystemResourceAsStream(fn)); + XMLStreamReader reader = factory.createXMLStreamReader(bis); + String tagContent; + boolean foundLink = false; + LOGGER.debug("Poczatek odczytu z pliku: " + fn); + while (reader.hasNext()) { + int eventType = reader.next(); + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + String str = reader.getLocalName(); + if ("url_do_pobrania".equals(str)) { + foundLink = true; + } + break; + + case XMLStreamConstants.CHARACTERS: + if (foundLink) { + tagContent = reader.getText().trim(); + String godlo = tagContent.substring(tagContent.lastIndexOf('_') + 1); + godlo = godlo.substring(0, godlo.indexOf('.')); + String old = map.remove(godlo); + map.put(godlo, tagContent); + foundLink = false; + } + break; + + case XMLStreamConstants.END_ELEMENT: + break; + + default: + break; + } + } + reader.close(); + is.close(); + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTReader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTReader.java new file mode 100644 index 0000000..77f880f --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/nmt/NMTReader.java @@ -0,0 +1,58 @@ +package pl.wat.ms4ds.terenfunkcje.nmt; + +import java.io.*; + +public class NMTReader { + + + public static void main(String[] args) { +// File dir = new File(System.getProperty("user.home") + "/nmt/gugik_SkorowidzNMT2018.gml"); + + InputStream is = null; + try { + File file = new File("C:/Workspace/nmt/N-34-139-A-b-2-4.asc"); + is = new FileInputStream(file); + readFromInputStream(is); + //... + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private static void readFromInputStream(InputStream inputStream) throws IOException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { + String line= br.readLine(); + String[] split = line.split(" "); + int ncols = Integer.parseInt(split[1]); + line= br.readLine(); + split = line.split(" "); + int nrows = Integer.parseInt(split[1]); + line= br.readLine(); + split = line.split(" "); + double xll = Double.parseDouble(split[1]); + line= br.readLine(); + split = line.split(" "); + double yll = Double.parseDouble(split[1]); + line= br.readLine(); + split = line.split(" "); + double cellsize = Double.parseDouble(split[1]); + line= br.readLine(); + split = line.split(" "); + double nodata = Double.parseDouble(split[1]); + while ((line = br.readLine()) != null) { + split = line.split(" "); + + } + } + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfField.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfField.java new file mode 100644 index 0000000..f23bc07 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfField.java @@ -0,0 +1,37 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +public class DbfField { + + /** + * Field name with a maximum of 10 characters. If less than 10, it is padded with null characters (0x00). + */ + String name; + + /** + * Field type: + * C – Character/String + * Y – Currency + * N – Numeric/integer + * F – Float + * D – Date + * T – DateTime + * B – Double + * I – Integer + * L – Logical + * M – Memo + * G – General + * M – Memo (binary) P – Picture Q – Varbinary V – Varchar (binary) + */ + char type; + + /** + * Number of decimal places. + */ + int size; + + DbfField(String name, char type, int size) { + this.name = name; + this.type = type; + this.size = size; + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfHeader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfHeader.java new file mode 100644 index 0000000..d1b05de --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfHeader.java @@ -0,0 +1,80 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.io.BufferedInputStream; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Shape File Header Reader.

+ * used for *.dbf-files.
+ * + * @author jrulka + */ +public class DbfHeader { + int fileType; + int dateY; + int dateM; + int dateD; + int numberOfRecords; + /** + * Position of first data record + */ + int dataPos; + /** + * Length of one data record, including delete flag + */ + int recordSize; + + static final int SIZE_BYTES = 32; + + DbfField[] fields; + + /** + * Constructs a new ShapeFileHeader. + * + */ + public DbfHeader(BufferedInputStream bis) throws Exception { + byte[] data = new byte[32]; + if (bis.read(data) != 32) { + throw new Exception("Invalid dbf file"); + } + ByteBuffer bb = ByteBuffer.wrap(data); + // MAIN FILE HEADER + bb.order(ByteOrder.LITTLE_ENDIAN); + fileType = bb.get(0); + dateY = bb.get(1) + 1900; + dateM = bb.get(2); + dateD = bb.get(3); + numberOfRecords = bb.getInt(4); + dataPos = bb.getShort(8); + recordSize = bb.getShort(10); + // start of fields + int numFields = (dataPos - 32 - 1) / SIZE_BYTES; + fields = new DbfField[numFields]; + for (int i = 0; i < numFields; i++) { + if (bis.read(data) != 32) { + throw new Exception("Invalid dbf file"); + } + bb = ByteBuffer.wrap(data); + byte[] fieldData = new byte[11]; //0-11 + bb.get(fieldData); + String name = null; + try { + name = new String(fieldData, "UTF-8"); + name = name.substring(0, name.indexOf('\0')); // get proper name + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + char type = (char) bb.get(); + bb.getInt(); + int size = bb.get() & 0xFF; // so we get values from 0-255. + fields[i] = new DbfField(name, type, size); + } + data = new byte[1]; + if (bis.read(data) != 1) { + throw new Exception("Invalid dbf file"); + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfRecord.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfRecord.java new file mode 100644 index 0000000..de2d598 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/DbfRecord.java @@ -0,0 +1,68 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.io.BufferedInputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +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/terenfunkcje/osm/shapefile/Main.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/Main.java new file mode 100644 index 0000000..255636e --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/Main.java @@ -0,0 +1,24 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + + +public class Main { + + public static void main(String[] args) { + try { + // GET DIRECTORY + String curDir = System.getProperty("user.dir"); + curDir = ""; + 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/terenfunkcje/osm/shapefile/OsmShapeFileReader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/OsmShapeFileReader.java new file mode 100644 index 0000000..f6ba74b --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/OsmShapeFileReader.java @@ -0,0 +1,125 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +/** + * A Shapefile (SHP) is a common geospatial vector data format for GIS software, developed by Esri. + * It allaws to store geographic features (points, lines, polygons) and their attributes in a collection of + * linked files, including .shp (geometry), .shx (index), and .dbf (attributes). + *

+ * All these files must be in the same folder and start with the same name.
+ * example: "my_shapefile.dbf", "my_shapefile.shp", "my_shapefile.shx"
+ *
+ * http://en.wikipedia.org/wiki/Shapefile"
+ * + * @author jrulka + * + */ +public class OsmShapeFileReader { + + ShpHeader shpHeader; + DbfHeader dbfHeader; + BufferedInputStream bisShp; + BufferedInputStream bisDbf; + + // pollyline: + // layer roads: + // layer waterways: + // layer railway: + // + // pollygon: + // layer buildings: fclass: 15xx: buildings + // layer landuse: + // fclass code + // forest 7201 + // park 7202 + // residential 7203 + // industrial 7204 + + // layer water: + // fclass code + // water 8000 + // reservoir 8201 + // river 8202 + // dock 8203 + // glacier 8211 + // wetland 8221 + + /** + *

+     * init the ShapeFile, and load the following files:
+     *   "path + filename.shx",
+     *   "path + filename.dbf",
+     *   "path + filename.shp"
+     * 
+ * + * @param path + * @param filename + * @throws Exception + */ + 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); + shpHeader = new ShpHeader(bisShp); + File dbfFile = new File(dir, filename + ".dbf"); + bisDbf = new BufferedInputStream(new FileInputStream(dbfFile)); + dbfHeader = new DbfHeader(bisDbf); + } + + public ShpShape nextShape() throws Exception { + boolean hasNext = false; + try { + hasNext = hasNextShape(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (!hasNext) { + return null; + } + ShpShape shape = null; + DbfRecord info = null; + 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; +// case PolyLine, PolyLineZ, PolyLineM: +// shape = new ShpPolyLine(shpHeader.shapeType); +// shape.read(bisShp); +// info = new DbfRecord(); +// info.read(bisDbf, dbfHeader); +// shape.setInfo(info); +// return shape; +// case Polygon, PolygonZ, PolygonM: +// shape = new ShpPolygon(shpHeader.shapeType); +// shape.read(bisShp); +// info = new DbfRecord(); +// info.read(bisDbf, dbfHeader); +// shape.setInfo(info); +// return shape; +// case MultiPoint, MultiPointZ, MultiPointM: +// shape = new ShpMultiPoint(shpHeader.shapeType); +// shape.read(bisShp); +// info = new DbfRecord(); +// info.read(bisDbf, dbfHeader); +// shape.setInfo(info); +// return shape; + default: + 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/terenfunkcje/osm/shapefile/ShpHeader.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpHeader.java new file mode 100644 index 0000000..9161b54 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpHeader.java @@ -0,0 +1,128 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Shape File Header Reader.

+ * used for *.shp-files and *.shx-files.
+ * contains information about:
+ * ...
+ * Shape-Type
+ * boundingbox
+ * measure-range
+ * ...
+ * + * @author jrulka + */ +public class ShpHeader { + final static int SHAPE_FILE_CODE = 9994; + final static int SHAPE_FILE_VERSION = 1000; + + int fileLength; + int shapeTypeCode; + double[][] bbox = new double[3][2]; // [x, y, z][min, max] + double[] rangeM = new double[2]; // [min, max] + + ShpShape.Type shapeType = null; + + /** + * Constructs a new ShapeFileHeader. + * + */ + public ShpHeader(BufferedInputStream bis) throws Exception { + byte[] data = new byte[100]; + if (bis.read(data) != 100) { + throw new Exception("Invalid shp file"); + } + ByteBuffer bb = ByteBuffer.wrap(data); + // MAIN FILE HEADER + bb.order(ByteOrder.BIG_ENDIAN); + // magic number + if (bb.getInt(0) != SHAPE_FILE_CODE) { + throw new IOException("(ShapeFile) error: SHP_MAGIC = " + SHAPE_FILE_CODE); + } + // file length + fileLength = bb.getInt(24); + bb.order(ByteOrder.LITTLE_ENDIAN); + // shp version + if (bb.getInt(28) != SHAPE_FILE_VERSION) { + throw new IOException("(ShapeFile) error: SHP_VERSION = " + SHAPE_FILE_VERSION); + } + shapeTypeCode = bb.getInt(32); + try { + shapeType = ShpShape.Type.getByCode(shapeTypeCode); + } catch (Exception e) { + e.printStackTrace(); + } + bbox[0][0] = bb.getDouble(36); // x-min + bbox[1][0] = bb.getDouble(44); // y-min + bbox[0][1] = bb.getDouble(52); // x-max + bbox[1][1] = bb.getDouble(60); // y-max + bbox[2][0] = bb.getDouble(68); // z-min + bbox[2][1] = bb.getDouble(76); // z-max + rangeM[0] = bb.getDouble(84); // m-min + rangeM[1] = bb.getDouble(92); // m-max + } + + /** + * get the type ShapeType the shapeFile contains.
+ * a shapeFile contains only one type of shape.
+ * + * @return ShpShape.Type + */ + public ShpShape.Type getShapeType() { + return shapeType; + } + + /** + * data storage: [3][2] --> [x,y,z][min, max]. + * + * @return boundingbox as double[][] + */ + public double[][] getBoundingBox() { + return bbox; + } + + /** + * get measure range.
+ * data storage: [2] --> [min, max] + * + * @return double[] + */ + public double[] getMeasureRange() { + return rangeM; + } + + /** + * get length in bytes of the shapeFile. + * + * @return length in bytes. + */ + public int getFileLengthBytes() { + return fileLength; + } + + /** + * get Verions on the shapeFile. + * + * @return should return 1000. + */ + public int getVersion() { + return SHAPE_FILE_VERSION; + } + + /** + * get MAGIC NUMBER of shapeFile. + * + * @return should return 9994. + */ + public int getMagicNumber() { + return SHAPE_FILE_CODE; + } + + + public void print() { + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpMultiPoint.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpMultiPoint.java new file mode 100644 index 0000000..41901e4 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpMultiPoint.java @@ -0,0 +1,70 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.nio.ByteBuffer; + +/** + * Shape: MultiPoint.
+ *
+ * A MultiPoint represents a set of points.
+ * Possible ShapeTypes:
+ *   MultiPoint   (  8 ),
+ *   MultiPointZ  ( 18 ),
+ *   MultiPointM  ( 28 ),
+ * 
+ * + * @author jrulka + * + */ +public class ShpMultiPoint extends ShpShape { + double xMin; + double yMin; + double zMin; + double mMin; + double xMax; + double yMax; + double zMax; + double mMax; + int numberOfPoints; + double[] pointsX; + double[] pointsY; + double[] pointsZ; + double[] valuesM; + + public ShpMultiPoint(ShpShape.Type shapeType) { + super(shapeType); + } + + @Override + protected void readRecordContent(ByteBuffer bb) { + xMin = bb.getDouble(); // x-min + yMin = bb.getDouble(); // y-min + xMax = bb.getDouble(); // x-max + yMax = bb.getDouble(); // y-max + numberOfPoints = bb.getInt(); // number of points (total of all parts) + pointsX = new double[numberOfPoints]; + pointsY = new double[numberOfPoints]; + pointsZ = new double[numberOfPoints]; + for (int i = 0; i < numberOfPoints; i++) { + pointsX[i] = bb.getDouble(); // x - coordinate + pointsY[i] = bb.getDouble(); // y - coordinate + } + // if SHAPE-TYPE: 18 + if (type.hasZvalues()) { + zMin = bb.getDouble(); // z-min + zMax = bb.getDouble(); // z-max + for (int i = 0; i < numberOfPoints; i++) { + pointsZ[i] = bb.getDouble(); // z - coordinate + } + } + // if SHAPE-TYPE: 18 | 28 + if (type.hasMvalues()) { + mMin = bb.getDouble(); // m-min + mMax = bb.getDouble(); // m-max + valuesM = new double[numberOfPoints]; + for (int i = 0; i < numberOfPoints; i++) { + valuesM[i] = bb.getDouble(); // m - value + } + } + } + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPoint.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPoint.java new file mode 100644 index 0000000..93e739c --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPoint.java @@ -0,0 +1,45 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.nio.ByteBuffer; + +/** + * Shape: Point.
+ *
+ * possible ShapeTypes:
+ *   Point   (  1 ),
+ *   PointZ  ( 11 ),
+ *   PointM  ( 21 ),
+ * 
+ * + * @author jrulka + * + */ + +public class ShpPoint extends ShpShape { + + // SHAPE RECORD CONTENT + double x; + double y; + double z; + double m; //[m-value] + + public ShpPoint(ShpShape.Type shapeType) { + super(shapeType); + } + + @Override + protected void readRecordContent(ByteBuffer bb) { + x = bb.getDouble(); // x - coordinate + y = bb.getDouble(); // y - coordinate + // if SHAPE-TYPE: 11 + if (type.hasZvalues()) { + z = bb.getDouble(); // z - coordinate + } + // if SHAPE-TYPE: 11 | 21 + if (type.hasMvalues()) { + m = bb.getDouble(); // m - value + } + } + + +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPollyShape.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPollyShape.java new file mode 100644 index 0000000..2e083ab --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPollyShape.java @@ -0,0 +1,66 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.nio.ByteBuffer; + +/** + * Base class for PollyLine, Pollygon + */ +public abstract class ShpPollyShape extends ShpShape { + // SHAPE RECORD CONTENT + double xMin; + double yMin; + double zMin; + double mMin; + double xMax; + double yMax; + double zMax; + double mMax; + private int numberOfParts; + private int numberOfPoints; + private 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) { + super(shape_type); + } + + @Override + protected void readRecordContent(ByteBuffer bb) { + xMin = bb.getDouble(); // x-min + yMin = bb.getDouble(); // y-min + xMax = bb.getDouble(); // x-max + yMax = bb.getDouble(); // y-max + numberOfParts = bb.getInt(); // number of polygon-parts / rings + numberOfPoints = bb.getInt(); // number of points (total of all parts) + partsPos = new int[numberOfParts]; + for (int i = 0; i < numberOfParts; i++) { + partsPos[i] = bb.getInt(); // index of the point-list (indicates start-point of a polygon) + } + pointsX = new double[numberOfPoints]; + pointsY = new double[numberOfPoints]; + for (int i = 0; i < numberOfPoints; i++) { + pointsX[i] = bb.getDouble(); // x - coordinate + pointsY[i] = bb.getDouble(); // y - coordinate + } + // if SHAPE-TYPE: 13 + if (type.hasZvalues()) { + zMin = bb.getDouble(); // z-min + zMax = bb.getDouble(); // z-max + for (int i = 0; i < numberOfPoints; i++) { + pointsZ[i] = bb.getDouble(); // z - coordinate + } + } + // if SHAPE-TYPE: 13 | 23 + if (type.hasMvalues()) { + mMin = bb.getDouble(); // m-min + mMax = bb.getDouble(); // m-max + valuesM = new double[numberOfPoints]; + for (int i = 0; i < numberOfPoints; i++) { + valuesM[i] = bb.getDouble(); // m - value + } + } + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolyLine.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolyLine.java new file mode 100644 index 0000000..7d562f4 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolyLine.java @@ -0,0 +1,28 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.nio.ByteBuffer; +import java.util.Locale; + +/** + * Shape: PolyLine.
+ *
+ * polyline: consists of one or more parts.
+ * part:     connected sequence of two or more points.
+ *           may or may not be connected to one another.
+ *           may or may not intersect one another.
+ * same content as polygon.
+ * possible ShapeTypes:
+ *   PolyLine   (  8 ),
+ *   PolyLineZ  ( 18 ),
+ *   PolyLineM  ( 28 ),
+ * 
+ * + * @author jrulka + * + */ +public class ShpPolyLine extends ShpPollyShape { + + public ShpPolyLine(Type shape_type) { + super(shape_type); + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolygon.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolygon.java new file mode 100644 index 0000000..e46d239 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpPolygon.java @@ -0,0 +1,26 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.nio.ByteBuffer; + +/** + * Shape: Polygon.
+ *
+ * polygon:   consists of one or more rings (multiple outer rings).
+ * ring:      four or more points ... closed, non-self-intersecting loop.
+ * interiour: clockwise order of vertices.
+ * same content as PolyLine.
+ * possible ShapeTypes:
+ *   Polygon   (  8 ),
+ *   PolygonZ  ( 18 ),
+ *   PolygonM  ( 28 ),
+ * 
+ * + * @author jrulka + * + */ +public class ShpPolygon extends ShpPollyShape { + + public ShpPolygon(Type shape_type) { + super(shape_type); + } +} diff --git a/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpShape.java b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpShape.java new file mode 100644 index 0000000..a2e4441 --- /dev/null +++ b/src/main/java/pl/wat/ms4ds/terenfunkcje/osm/shapefile/ShpShape.java @@ -0,0 +1,195 @@ +package pl.wat.ms4ds.terenfunkcje.osm.shapefile; + +import java.io.BufferedInputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Base class for Shapes. + * + * @author jrulka + * + */ +public abstract class ShpShape { + protected Type type; + + //RECORD HEADER + protected int recordNumber; + protected int contentLength; + protected int shapeTypeCode; + + DbfRecord info; + + protected ShpShape(Type type) { + this.type = type; + } + + /** + * read the shape-data from the bytebuffer (buffer-position has to be defined before).
+ * + */ + public void read(BufferedInputStream bis) throws Exception { + byte[] data = new byte[8]; + if (bis.read(data) != 8) { + throw new Exception("Invalid shp file"); + } + ByteBuffer bb = ByteBuffer.wrap(data); + // 1) READ RECORD HEADER + readRecordHeader(bb); + // Content length in words (16 bits) + byte[] dataContent = new byte[contentLength * 2]; + if (bis.read(dataContent) != contentLength * 2) { + throw new Exception("Invalid shp file"); + } + bb = ByteBuffer.wrap(dataContent); + // 2) READ RECORD CONTENT + // 2.1) check Shape Type + bb.order(ByteOrder.LITTLE_ENDIAN); + shapeTypeCode = bb.getInt(); + try { + Type shape_type = Type.getByCode(shapeTypeCode); + if (shape_type == type) { + readRecordContent(bb); + } else if (shape_type != type) { + throw new Exception("(Shape) shape_type = " + shape_type + ", but expected " + type); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected void readRecordHeader(ByteBuffer bb) { + bb.order(ByteOrder.BIG_ENDIAN); + recordNumber = bb.getInt(); + contentLength = bb.getInt(); + } + + protected abstract void readRecordContent(ByteBuffer bb); + + void setInfo(DbfRecord info) { + this.info = info; + } + + /** + * get the record number of the shape. + * + * @return record number + */ + public int getRecordNumber() { + return recordNumber; + } + + /** + * get the Type of the Shape. + * + * @return ShpShape.Type + */ + public Type getShapeType() { + return type; + } + + //---------------------------------------------------------------------------- + // Shape Types + //---------------------------------------------------------------------------- + public enum Type { + /** + * ID= 0 + */ + NullShape(0, false, false), + /** + * ID= 1 + */ + Point(1, false, false), + /** + * ID=11 + */ + PointZ(11, true, true), + /** + * ID=21 + */ + PointM(21, false, true), + /** + * ID= 3 + */ + PolyLine(3, false, false), + /** + * ID=13 + */ + PolyLineZ(13, true, true), + /** + * ID=23 + */ + PolyLineM(23, false, true), + /** + * ID= 5 + */ + Polygon(5, false, false), + /** + * ID=15 + */ + PolygonZ(15, true, true), + /** + * ID=25 + */ + PolygonM(25, false, true), + /** + * ID= 8 + */ + MultiPoint(8, false, false), + /** + * ID=18 + */ + MultiPointZ(18, true, true), + /** + * ID=28 + */ + MultiPointM(28, false, true), + /** + * ID=31 + */ + MultiPatch(31, true, true); + + + public final int code; + boolean has_z_values; + boolean has_m_values; + + 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; + this.code = code; + } + + public static Type getByCode(int ID) throws Exception { + for (Type st : Type.values()) + if (st.code == ID) + return st; + throw new Exception("ShapeType: " + ID + " does not exist"); + } + + public boolean hasZvalues() { + return has_z_values; + } + + public boolean hasMvalues() { + return has_m_values; + } + + public boolean isTypeOfPolygon() { + return (this == Type.Polygon | this == Type.PolygonM | this == Type.PolygonZ); + } + + public boolean isTypeOfPolyLine() { + return (this == Type.PolyLine | this == Type.PolyLineM | this == Type.PolyLineZ); + } + + public boolean isTypeOfPoint() { + return (this == Type.Point | this == Type.PointM | this == Type.PointZ); + } + + public boolean isTypeOfMultiPoint() { + return (this == Type.MultiPoint | this == Type.MultiPointM | this == Type.MultiPointZ); + } + } + +} diff --git a/teren.properties b/teren.properties new file mode 100644 index 0000000..39c22f8 --- /dev/null +++ b/teren.properties @@ -0,0 +1,76 @@ +#Polska +#Wspolrzedne referencyjne i wielkosc obszaru +x_ref=14 +y_ref=49 +dx_ref=11 +dy_ref=7 +#kwadraty_dir=c:/Workspace/git/teren-funkcje/au2data/new_teren/Polska/kwadraty/ +kwadraty_dir=C:/Workspace/_data/swdt/ms4ds/teren/kwadraty/ +drogi_dir=au2data/new_teren/Polska/drogi/ + +#Afganistan +#Wspolrzedne referencyjne i wielkosc obszaru +#x_ref=60 +#y_ref=29 +#dx_ref=15 +#dy_ref=10 +#kwadraty_dir=/au2data/new_teren/Afganistan/kwadraty/ +#drogi_dir=/au2data/new_teren/Afganistan/drogi/ + +#Rozdzielczosc terenu dl_mk=200 | 100 | 50 | 25 | 20 +dl_mk=100 + +#W celu wymuszenia (mimo jej braku) przejezdności terenu nalezy ustawić na: on +przejezdnosc_zawsze=off +minimalny_stopien_przejezdnosci=0.1 + +#Minimalny stopień przejezdności dla ruchu na przełaj dla kwadratów przejezdnych +#dla algorytmów wyznaczania dróg dla działań typu: atak, obrona, rozmieszczenie, ... +stopien_przejezdnosci.minimalny_na_przelaj=0.7 +#Minimalny stopień przejezdności dla ruchu po drodze z uwzględnieniem nachylenia terenu +stopien_przejezdnosci.minimalny.na_drodze.nachylenie_terenu=0.9 +#Minimalny stopień przejezdności dla ruchu na przełaj z uwzględnieniem nachylenia terenu +#Dla kątów <-alfa_max, alfa_min> stopień przejezdności == 1 +#Dla kątów stopień przejezdności == liniowo (względem tangensa kąta) maleje od 1 do tej wartości +#w p.p. stopień przejezdności == 0 +stopien_przejezdnosci.minimalny.na_przelaj.nachylenie_terenu=0.1 +#Minimalny kąt nachylenia terenu wpływający na stopień przejezdności (alfa_min) dla ruchu na drodze [stopnie] +stopien_przejezdnosci.na_drodze.nachylenie_terenu.kat_minimalny=15 +#Maksymalny kąt nachylenia terenu wpływający na stopień przejezdności (alfa_max) dla ruchu na drodze [stopnie] +stopien_przejezdnosci.na_drodze.nachylenie_terenu.kat_maksymalny=50 +#Minimalny kąt nachylenia terenu wpływający na stopień przejezdności (alfa_min) dla ruchu na przełaj [stopnie] +stopien_przejezdnosci.na_przelaj.nachylenie_terenu.kat_minimalny=15 +#Maksymalny kąt nachylenia terenu wpływający na stopień przejezdności (alfa_min) dla ruchu na przełaj [stopnie] +stopien_przejezdnosci.na_przelaj.nachylenie_terenu.kat_maksymalny=45 + +#stopień przejezdności - parametr dla symulacji ruchu +stopien_przejezdnosci.podwozie_gasienicowe.teren_zabudowany=0.8 +stopien_przejezdnosci.podwozie_gasienicowe.teren_zalesiony=0.25 +stopien_przejezdnosci.podwozie_gasienicowe.teren_zabagniony=0.2 +stopien_przejezdnosci.podwozie_gasienicowe.teren_zawodniony=0.0 +stopien_przejezdnosci.podwozie_gasienicowe.teren_czysty=1.0 + +stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zabudowany=0.7 +stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zalesiony=0.15 +stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zabagniony=0.1 +stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_zawodniony=0.0 +stopien_przejezdnosci.podwozie_kolowo_gasienicowe.teren_czysty=0.9 + +stopien_przejezdnosci.podwozie_kolowe.teren_zabudowany=0.6 +stopien_przejezdnosci.podwozie_kolowe.teren_zalesiony=0.1 +stopien_przejezdnosci.podwozie_kolowe.teren_zabagniony=0.05 +stopien_przejezdnosci.podwozie_kolowe.teren_zawodniony=0.0 +stopien_przejezdnosci.podwozie_kolowe.teren_czysty=0.8 + +stopien_przejezdnosci.podwozie_poduszka.teren_zabudowany=0.7 +stopien_przejezdnosci.podwozie_poduszka.teren_zalesiony=0.1 +stopien_przejezdnosci.podwozie_poduszka.teren_zabagniony=0.9 +stopien_przejezdnosci.podwozie_poduszka.teren_zawodniony=1.0 +stopien_przejezdnosci.podwozie_poduszka.teren_czysty=1.0 + +stopien_przejezdnosci.podwozie_plozy.teren_zabudowany=0.1 +stopien_przejezdnosci.podwozie_plozy.teren_zalesiony=0.1 +stopien_przejezdnosci.podwozie_plozy.teren_zabagniony=0.1 +stopien_przejezdnosci.podwozie_plozy.teren_zawodniony=0.0 +stopien_przejezdnosci.podwozie_plozy.teren_czysty=1.0 +