GeoreferencesManager.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('EpsgConversion');
  4. class Route_GeoreferencesManager extends RouteBase {
  5. protected $ACTION, $REFERER;
  6. const TABLE = "WMS_MAP_GEOREFERENCES";
  7. public function handleAuth() {
  8. if (!User::logged()) {
  9. User::authByRequest();
  10. }
  11. $this->REFERER = $_SERVER['HTTP_REFERER'];
  12. $this->ACTION = V::get('action', '', $_GET, '');
  13. }
  14. public function defaultAction() {
  15. SE_Layout::gora();
  16. SE_Layout::menu();
  17. ?>
  18. <div class="container" style="margin-top:20px">
  19. <legend>Zarządzanie punktami georeferencyjnymi</legend>
  20. <div class="form-group">
  21. <div class="col-sm-12">
  22. <a href="?_route=GeoreferencesManager&action=verifyPoints" class="btn-sm btn-primary">Zweryfikuj punkty</a>
  23. <a href="?_route=GeoreferencesManager&action=uploadPoints" class="btn-sm btn-primary">Wgraj punkty</a><?=($this->ACTION ? "<hr/>" : "")?>
  24. </div>
  25. <div class="col-sm-12">
  26. <?php
  27. switch ($this->ACTION) {
  28. case "verifyPoints":
  29. $this->verifyPoints();
  30. break;
  31. case "repairPoints":
  32. $this->repairPoints();
  33. break;
  34. case "uploadPoints":
  35. $this->uploadPoints();
  36. break;
  37. }
  38. ?>
  39. </div>
  40. </div>
  41. </div>
  42. <?php
  43. SE_Layout::dol();
  44. }
  45. public function reinstallAction() {
  46. $this->reinstall();
  47. die('OK');
  48. }
  49. public function reinstall() {
  50. }
  51. private static function getBadPoints() {
  52. $points = DB::getPDO()->fetchall("select `ID`, `EPSG`, x(`the_geom`) as `gx`, y(`the_geom`) as `gy`, `x`, `y` from `" . self::TABLE . "`");
  53. $errors = [];
  54. foreach ($points as $point) {
  55. if ($point['x'] > 0 && $point['y'] > 0) {
  56. if ($point['gx'] && $point['gy']) {
  57. try {
  58. $test = epsgConversion::LonLatToPUWGWGS84($point['gx'], $point['gy']);
  59. if (abs($point['x'] - $test->x) > 0.001) $errors["unfuckable"][$point["ID"]]["errors"][] = "x";
  60. if (abs($point['y'] - $test->y) > 0.001) $errors["unfuckable"][$point["ID"]]["errors"][] = "y";
  61. if ($point['EPSG'] != $test->epsg) $errors["unfuckable"][$point["ID"]]["errors"][] = "epsg";
  62. if (isset($errors["unfuckable"][$point["ID"]])) $errors["unfuckable"][$point["ID"]]["data"] = $point;
  63. } catch (Exception $e) {
  64. $errors["nonunfuckable"][$point["ID"]]["data"] = $point;
  65. }
  66. } else $errors["nonunfuckable"][$point["ID"]]["data"] = $point;
  67. } elseif (!($point['gx'] && $point['gy'])) $errors["nonunfuckable"][$point["ID"]]["data"] = $point;
  68. }
  69. return $errors;
  70. }
  71. private function verifyPoints() {
  72. $errorsData = self::getBadPoints();
  73. if (isset($errorsData["unfuckable"])) {
  74. $echo = '<b>Znalezione błędy, które można automatycznie naprawić:</b></br/><pre>';
  75. foreach ($errorsData["unfuckable"] as $error) {
  76. $ok = epsgConversion::PUWGToLonLatWGS84($error['data']['x'], $error['data']['y']);
  77. $echo .= "[{$error['data']['ID']}] x = <span style='color:" . ((in_array("x", $error["errors"])) ? "red" : "green") .
  78. ";'>{$error['data']['gx']}</span> ({$ok->x}), y = <span style='color:" . ((in_array("y", $error["errors"])) ? "red" : "green") .
  79. ";'>{$error['data']['gy']}</span> ({$ok->y}), epsg = <span style='color:" . ((in_array("epsg", $error["errors"])) ? "red" : "green") .
  80. ";'>{$error['data']['EPSG']}</span>" . ((in_array("epsg", $error["errors"])) ? " ({$ok->epsg})" : "") . "<br/>";
  81. }
  82. $echo .= '</pre><div style="text-align:center;"><a href="?_route=GeoreferencesManager&action=repairPoints" class="btn btn-primary">Napraw wszystkie</a></div>';
  83. SE_Layout::alert('warning', $echo);
  84. }
  85. if (isset($errorsData["nonunfuckable"])) {
  86. $echo = '<b>Znalezione błędy, których nie można automatycznie naprawić:</b></br/><pre>';
  87. foreach ($errorsData["nonunfuckable"] as $error) {
  88. $echo .= "[{$error['data']['ID']}] the_geom = " . ((!($error['data']['gx'] || $error['data']['gy'])) ? "NULL" : "POINT({$error['data']['gx']} {$error['data']['gy']})") .
  89. ", x = {$error['data']['x']}, y = {$error['data']['y']}, epsg = {$error['data']['EPSG']}<br/>";
  90. }
  91. $echo .= '</pre>';
  92. SE_Layout::alert('danger', $echo);
  93. }
  94. if (!$errorsData) {
  95. SE_Layout::alert('success', "Wszystko OK");
  96. }
  97. }
  98. private function repairPoints() {
  99. $errorsData = self::getBadPoints()["unfuckable"];
  100. $updateErrors = [];
  101. $updateSuccess = 0;
  102. foreach ($errorsData as $ID => $error) {
  103. $ok = epsgConversion::PUWGToLonLatWGS84($error['data']['x'], $error['data']['y']);
  104. $sqlArr = [
  105. "ID" => $ID,
  106. "the_geom" => "GeomFromText('POINT({$ok->x} {$ok->y})')",
  107. "EPSG" => $ok->epsg
  108. ];
  109. try {
  110. if (DB::getDB()->UPDATE_OBJ(self::TABLE, $sqlArr) < 1) $updateErrors[] = $ID;
  111. else $updateSuccess++;
  112. } catch (Exception $e) {
  113. $updateErrors[] = $ID;
  114. }
  115. }
  116. $echo = "Zaktualizowano {$updateSuccess} z " . count($errorsData) . " rekordów.";
  117. if ($updateErrors) {
  118. $echo .= "<br/>Nie zaktualizowano rekordów ID: " . implode(", ", $updateErrors);
  119. SE_Layout::alert('danger', $echo);
  120. } else SE_Layout::alert('success', $echo);
  121. }
  122. private function uploadPoints() {
  123. $subActions = ["uploadPointsConfirm", "uploadPointsSave"];
  124. if (!in_array($subAction = V::get('subAction','',$_POST), $subActions)) $subAction = "uploadPointsForm";
  125. $this->$subAction();
  126. }
  127. private function uploadPointsConfirm() {
  128. try {
  129. $maxDistance = V::get('maxDistance','ERROR',$_POST);
  130. if (!is_numeric($maxDistance)) throw new Exception("Błąd formularza #1");
  131. if (!isset($_FILES['file'])) throw new Exception("Błąd formularza #2");
  132. if (!file_exists($_FILES['file']['tmp_name'])) throw new Exception("Wystąpił problem z przesłaniem pliku");
  133. if ($_FILES['file']['type'] != 'text/csv') throw new Exception("Błędny typ pliku - {$_FILES['file']['type']}");
  134. $file = file($_FILES['file']['tmp_name']);
  135. $points = [];
  136. foreach ($file as $line) {
  137. list($lp, $y, $x, $z, $type) = explode(',', $line);
  138. if (!($lp && $x && $y && $z && $type)) throw new Exception("Plik zawiera niepoprawne dane");
  139. if (trim($type) == "Pikieta") {
  140. foreach ($points as $key => $point) {
  141. if (sqrt((pow($point['x'] - $x, 2) + pow($point['y'] - $y, 2)) <= $maxDistance)) {
  142. $lp = $key;
  143. break;
  144. }
  145. }
  146. $points[$lp] = ['x' => $x, 'y' => $y, 'z' => $z];
  147. }
  148. }
  149. if (!$points) throw new Exception("Plik zawiera niepoprawne dane");
  150. $tempTbl = self::TABLE . "_temp";
  151. DB::getPDO()->query("CREATE TEMPORARY TABLE `{$tempTbl}` (SELECT * FROM `" . self::TABLE . "`)");
  152. $result = DB::getPDO()->fetchall("SELECT ID, x(the_geom) AS gx, y(the_geom) AS gy FROM `{$tempTbl}` WHERE ST_IsEmpty(the_geom) = 0 AND x = 0 AND y = 0");
  153. foreach ($result as $row) {
  154. $puwg = epsgConversion::LonLatToPUWGWGS84($row['gx'], $row['gy']);
  155. DB::getPDO()->query("UPDATE `{$tempTbl}` SET x = '{$puwg->x}', y = '{$puwg->y}' WHERE ID='{$row['ID']}'");
  156. }
  157. $duplicates = [];
  158. $closePoints = [];
  159. foreach ($points as $lp => $point) {
  160. $result = DB::getPDO()->fetchall("SELECT ID, SQRT(POW('{$point['x']}' - x, 2) + POW('{$point['y']}' - y, 2)) as distance, z FROM `{$tempTbl}` ORDER BY distance LIMIT 1");
  161. if ($result) {
  162. if (in_array($result[0]['ID'], $closePoints)) $duplicates[$lp] = array_search($result[0]['ID'], $closePoints);
  163. ($duplicates[$lp] = array_search($result[0]['ID'], $closePoints)) ?: $closePointsDetail[$lp] = $result[0];
  164. $closePoints[$lp] = $result[0]['ID'];
  165. }
  166. else $closePointsDetail[$lp] = false;
  167. $wgs84[$lp] = epsgConversion::PUWGToLonLatWGS84($point['x'], $point['y']);
  168. }
  169. ?>
  170. <style>
  171. <!--
  172. .table > tbody > tr > td {vertical-align: middle};
  173. -->
  174. </style>
  175. <form class="form-horizontal" method="post" enctype="multipart/form-data">
  176. <div class="form-group">
  177. <div class="col-sm-12">
  178. <table class="table table-bordered table-hover table-striped">
  179. <thead>
  180. <tr style="text-align:center; background-color:lightgray"><td>Lp.</td><td>X</td><td>Y</td><td>Z (m)</td><td>EPSG</td><td>Znaleziony najbliższy punkt (w odległości w metrach)</td><td>Działanie</td></tr>
  181. </thead>
  182. <tbody>
  183. <?php
  184. $i = 0;
  185. foreach ($points as $lp => $point) {
  186. $disabled = false;
  187. $checked = true;
  188. echo "<tr><td nowrap align='right'>" . ++$i . "</td><td nowrap>{$point['x']} ({$wgs84[$lp]->x})</td><td nowrap>{$point['y']} ({$wgs84[$lp]->y})</td><td nowrap>{$point['z']}</td><td nowrap align='center'>{$wgs84[$lp]->epsg}</td><td nowrap>";
  189. if ($closePointsDetail[$lp]) {
  190. if ($closePointsDetail[$lp]['distance'] == 0) {
  191. if ($closePointsDetail[$lp]['z']) {
  192. if ($closePointsDetail[$lp]['z'] == $point['z']) {
  193. echo "ID:{$closePoints[$lp]} - to ten sam punkt, nie ma co aktualiować";
  194. $disabled = true;
  195. } else echo "ID:{$closePoints[$lp]} - to ten sam punkt, ale z inną wartością Z -<br/>należy zaktualizować";
  196. } else echo "ID:{$closePoints[$lp]} - to ten sam punkt, ale wcześniej nie został zweryfikowany -<br/>należy zaktualiować";
  197. } elseif ($closePointsDetail[$lp]['distance'] <= $maxDistance) echo "ID:{$closePoints[$lp]} ({$closePointsDetail[$lp]['distance']}) - należy zaktualizować";
  198. else {
  199. echo "ID:{$closePoints[$lp]} ({$closePointsDetail[$lp]['distance']}) - za daleko, zdecyduj co zrobić";
  200. $checked = false;
  201. }
  202. } elseif (isset($duplicates[$lp])) {
  203. echo "ID:{$closePoints[$lp]} - ten sam punkt został znaleziony dla punktu Lp. {$duplicates[$lp]} -<br/>nie można zaktualizować z powodu konfliktu<br/>(zalecia się ponowne wgranie tego samego pliku po dodaniu pozostałych punktów)";
  204. $disabled = true;
  205. } else echo "Nie znaleziono żadnego punktu - należy dodać nowy";
  206. echo "</td><td nowrap>";
  207. if ($closePointsDetail[$lp]['distance'] <= $maxDistance) echo "<div class='checkbox'><label><input type='checkbox' name='points[{$lp}]'" .
  208. ($disabled ? " disabled" : ($checked ? " checked" : "")) . "/>" . ($closePoints[$lp] ? "Zaktualizuj" : "Dodaj nowy") . "</label></div>";
  209. else {
  210. ?>
  211. <div class='radio'><label><input type='radio' name='points[<?=$lp?>]' value='new' checked/>Dodaj nowy</label></div>
  212. <div class='radio'><label><input type='radio' name='points[<?=$lp?>]' value='on'/>Zaktualizuj</label></div>
  213. <div class='radio'><label><input type='radio' name='points[<?=$lp?>]' value='off'/>Zignoruj</label></div>
  214. <?php
  215. }
  216. echo "</td></tr>";
  217. }
  218. ?>
  219. </tbody>
  220. </table>
  221. <div style="text-align:center;"><button type="submit" class="btn btn-primary" name="subAction" value="uploadPointsSave">Zapisz</button></div>
  222. </div>
  223. </div>
  224. </form>
  225. <?php
  226. $_SESSION['uploadPointsData'] = gzcompress(json_encode(['points' => $points, 'closePoints' => $closePoints]));
  227. } catch (Exception $e) {
  228. SE_Layout::alert('danger', $e->getMessage());
  229. $this->uploadPointsForm();
  230. }
  231. }
  232. private function uploadPointsSave() {
  233. try {
  234. if (!isset($_SESSION['uploadPointsData'])) throw new Exception("Błąd danych #1");
  235. $data = json_decode(gzuncompress($_SESSION['uploadPointsData']), true);
  236. if (!$data) throw new Exception("Błąd danych #2");
  237. $points = V::get('points','',$_POST);
  238. if (!is_array($points)) throw new Exception("Błąd danych #3");
  239. $points = array_diff($points, ["off"]);
  240. if (!$points) throw new Exception("Nie zdefiniowany żadnych punktów do aktualizacji/dodania");
  241. foreach ($points as $lp => $action) {
  242. $x = $data['points'][$lp]['x'];
  243. $y = $data['points'][$lp]['y'];
  244. $z = $data['points'][$lp]['z'];
  245. try {
  246. $wgs84 = epsgConversion::PUWGToLonLatWGS84($x, $y);
  247. $gx = $wgs84->x;
  248. $gy = $wgs84->y;
  249. $epsg = $wgs84->epsg;
  250. $the_geom = "GeomFromText('POINT({$gx} {$gy})')";
  251. $sqlArrs[$lp] = [
  252. "A_STATUS" => "NORMAL",
  253. "A_STATUS_INFO" => "Punkt uzgodniony automatycznie",
  254. "the_geom" => $the_geom,
  255. "x" => $x,
  256. "y" => $y,
  257. "z" => $z,
  258. "EPSG" => $epsg
  259. ];
  260. if ($action == "on") {
  261. if (!isset($data['closePoints'][$lp])) throw new Exception("Błąd danych #3");
  262. $sqlArrs[$lp]['ID'] = $data['closePoints'][$lp];
  263. } elseif ($action != "new") throw new Exception("Błąd danych #4");
  264. } catch (Exception $e) {
  265. SE_Layout::alert('danger'," Wystąpił problem (#1) z dodaniem punktu Lp. {$lp} - {$e->getMessage()}");
  266. }
  267. }
  268. $i = 0;
  269. foreach ($sqlArrs as $lp => $sqlArr) {
  270. try {
  271. if (isset($sqlArr['ID'])) $affected = DB::getDB()->UPDATE_OBJ(self::TABLE, $sqlArr);
  272. else $affected = DB::getDB()->ADD_NEW_OBJ(self::TABLE, $sqlArr);
  273. if ($affected < 1) throw new Exception("Błąd bazy danych: " . print_r($sqlArr, true));
  274. $i++;
  275. } catch (Exception $e) {
  276. SE_Layout::alert('danger', "Wystąpił problem (#2) z dodaniem punktu Lp. {$lp} - {$e->getMessage()}");
  277. }
  278. }
  279. if ($i > 0) SE_Layout::alert(($i == count($sqlArrs) ? "success" : "warning"), "Pomyślnie dodano/zaktualizowano {$i} z " . count($sqlArrs) . " punktów");
  280. else SE_Layout::alert('danger', "Nie dodano/zaktualiowano żadnego punktu");
  281. } catch (Exception $e) {
  282. SE_Layout::alert('danger', $e->getMessage());
  283. $this->uploadPointsForm();
  284. }
  285. }
  286. private function uploadPointsForm() {
  287. ?>
  288. <form class="form-horizontal" method="post" enctype="multipart/form-data">
  289. <div class="form-group">
  290. <label class="col-sm-4 control-label">Wybierz plik z punktami georeferencyjnymi</label>
  291. <div class="col-sm-8" style="margin-top:7px;">
  292. <input type="hidden" name="subAction" value="uploadPointsConfirm">
  293. <input type="file" name="file" required/>
  294. </div>
  295. </div>
  296. <div class="form-group">
  297. <label class="col-sm-4 control-label">Maksymalna odległość istniejącego punktu (w metrach)</label>
  298. <div class="col-sm-1">
  299. <input type="number" class="form-control" name="maxDistance" data-bind="value:replyNumber" min="1" value="50" required>
  300. </div>
  301. </div>
  302. <div class="form-group">
  303. <div class="col-sm-offset-4 col-sm-8">
  304. <button type="submit" class="btn btn-primary" name="subAction" value="uploadPointsConfirm">Wgraj plik</button>
  305. </div>
  306. </div>
  307. </form>
  308. <?php
  309. }
  310. }