GeoreferencesManager.php 14 KB

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