<?php

$_SERVER['SERVER_NAME'] = 'biuro.biall-net.pl';

//when used in /Library/Server/Web/Data/Sites/bzyk-biale-plamy-php-temp/SE/dev on biuro.biall-net.pl
if( file_exists ( "../../se-lib/bootstrap.php"))
require("../../se-lib/bootstrap.php");
//when used in  in native SE
else require("../../../../SE-production-git/SE/se-lib/bootstrap.php");

Lib::loadClass("Vendor_Geophp");

class showProgress {

	private $startTime;
	private $i = 0, $l = 0, $left = 0, $diff = 0, $n, $msg;
	private $strlen = 50;

	public function __construct($msg, $n = null) {
		$this->msg = str_pad($msg, $this->strlen);
		echo "{$this->msg}:   0%";
		$this->n = $n;
		list($usec, $sec) = explode(" ", microtime());
		$this->startTime = $sec + $usec;
	}

	public function step($int = 1) {
		if ($this->n) {
			$this->i += $int;
			$p = floor(100 * $this->i / $this->n);
			list($usec, $sec) = explode(" ", microtime());
			$curTime = $sec + $usec;
			$diff = $curTime - $this->startTime;
			$left = floor((($this->n / $this->i) - 1) * $diff);
			$diff = floor($diff);
			if ($p > $this->l || $left != $this->left || $diff != $this->diff) {
				$this->l = $p;
				$this->diff = $diff;
				$this->left = $left;
				if ($left > 59) $left = floor($left / 60) . "m " . ($left % 60);
				if ($diff > 59) $diff = floor($diff / 60) . "m " . ($diff % 60);
				echo "\r".str_pad("", $this->strlen+70);
				echo "\r{$this->msg}: {$p}% (elapsed {$diff}s, estimated remaining {$left}s)";
			}
		}	
	}

	public function stop() {
		list($usec, $sec) = explode(" ", microtime());
		$curTime = $sec + $usec;
		$diff = $curTime - $this->startTime;
		$diff = floor($diff);
		if ($diff > 59) $diff = floor($diff / 60) . "m " . ($diff % 60);
		echo "\r".str_pad("", $this->strlen+70);
		echo "\r{$this->msg}: OK (done in {$diff}s)\n";
	}
}


class bialePlamy {

	private $showProgress = false;

	private $getSourceData = false, $createTemporaryWaysTable = false, $createTemporaryJoinsTable = false, $createTemporaryPrzylaczaTable = false, $readyForFindWay = false, $createTemporaryLongWaysTable = false;

	private $punktyAdresoweTable = null, $drogiTable = null;
	private $drogi_asText = array(), $joins_asText = array(), $ways_asText = array(), $xpoints_asText = array(), $points_asText = array(), $przylacza_asText = array(), $waysLengths = array(), $longWays_asText = array();
	private $ways2drogi = array(), $drogi2ways = array(), $ways2joins = array(), $joins2ways = array(), $points2xpoints = array(), $xpoints2points = array(), $przylacza2joins = array(), $przylacza2points = array(), $points2joins = array();

	public function __construct($sourceTables) {
		$tables = array('pointsTable' => 'punktyAdresoweTable', 'origWaysTable' => 'drogiTable');
		try {
			if (!is_array($sourceTables)) throw new Exception("bialePlamy :: Wrong parameters");
			foreach ($tables as $arg => $table) {
				if (!isset($sourceTables[$arg])) throw new Exception("bialePlamy :: Missing parameter {$arg}");
				$this->$table = $sourceTables[$arg];
			}
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function enableShowProgress() {
		$this->showProgress = true;
	}

	public function disableShowProgress() {
		$this->showProgress = false;
	}

	private $dataArrays = array('drogi_asText', 'joins_asText', 'ways_asText', 'xpoints_asText', 'points_asText', 'przylacza_asText', 'waysLengths', 'longWays_asText',
		'ways2drogi', 'drogi2ways', 'ways2joins', 'joins2ways', 'points2xpoints', 'xpoints2points', 'przylacza2joins', 'przylacza2points', 'points2joins');

	public function saveCache($prefix = '') {
		if (!preg_match("/^[[:alnum:]]*$/", $prefix)) throw new Exception("saveCache() :: prefix error");
		if ($prefix) $prefix .= "_";
		foreach ($this->dataArrays as $array) if ($this->$array) {
			if ($this->showProgress) $progress = new showProgress("Saving {$array}");
			file_put_contents('/tmp/save_'.$prefix.$array.'.txt', serialize($this->$array));
			if ($this->showProgress) $progress->stop();
		}
	}

	public function saveXML() {
		function array_to_xml($data, &$xml_data) {
			foreach ($data as $key => $value) {
				$key_ = "";
				if (is_numeric($key)) $key_ = 'item';
				if (is_array($value)) {
					$subnode = $xml_data->addChild($key_);
					$subnode->addAttribute('id', $key);
					array_to_xml($value, $subnode);
				} else {
					$subnode = $xml_data->addChild("$key_", htmlspecialchars("$value"));
					if (is_numeric($key)) $subnode->addAttribute('id', $key);
				}
			}
		}

		foreach ($this->dataArrays as $array) if ($this->$array) {
			if ($this->showProgress) $progress = new showProgress("Generating XML for {$array}");
			$xml = new SimpleXMLElement("<?xml version=\"1.0\"?><{$array}></{$array}>");
			array_to_xml($this->$array, $xml);
			$xml->asXML("{$array}.xml");
			if ($this->showProgress) $progress->stop();
		}
	}

	public function printArray($array) {
		if (isset($this->$array) && is_array($this->$array)) {
			return print_r($this->$array, true);
		} else return null;
	}

	public function saveArrays() {
		foreach ($this->dataArrays as $array) if ($this->$array) {
			if ($this->showProgress) $progress = new showProgress("Saving array {$array} to file");
			file_put_contents("/tmp/array_{$array}.txt", $this->printArray($array));
			if ($this->showProgress) $progress->stop();
		}
	}

	public function loadCache($prefix = '') {
		try {
			if (!preg_match("/^[[:alnum:]]*$/", $prefix)) throw new Exception("saveCache() :: prefix error");
			if ($prefix) $prefix .= "_";
			foreach ($this->dataArrays as $array) if (file_exists('/tmp/save_'.$prefix.$array.'.txt')) {
				if ($this->showProgress) $progress = new showProgress("Loading {$array}");
				$this->$array = unserialize(file_get_contents('/tmp/save_'.$prefix.$array.'.txt'));
				if ($this->showProgress) $progress->stop();
			}
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function saveDB($src, $dst) {
		try {
			if ($this->showProgress) $progress = new showProgress("Saving table {$dst}");
			DB::getPDO()->query("truncate table `{$dst}`");
			$result = DB::getPDO()->fetchall("show fields from `{$src}`");
			foreach ($result as $col) $cols[] = "`{$col['Field']}`";
			$query = "insert into `{$dst}` (".implode(", ", $cols).") select * from `{$src}`";
			DB::getPDO()->query($query);
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function savePointsTable($dst) {
		if (!$this->getSourceData) $this->getSourceData();
		if ($this->xpoints2points) $this->updateTemporaryPointsTable();
		$this->saveDB("_temp_punkty_adresowe", $dst);
	}

	public function saveWaysTable($dst) {
		if (!$this->createTemporaryWaysTable) $this->createTemporaryWaysTable();
		$this->saveDB("_temp_ways", $dst);
	}

	public function saveJoinsTable($dst) {
		if (!$this->createTemporaryJoinsTable) $this->createTemporaryJoinsTable();
		$this->saveDB("_temp_joins", $dst);
	}

	public function savePrzylaczaTable($dst) {
		if (!$this->createTemporaryPrzylaczaTable) $this->createTemporaryPrzylaczaTable();
		$this->saveDB("_temp_przylacza", $dst);
	}

	public function saveLongWaysTable($dst) {
		if (!$this->createTemporaryLongWaysTable) $this->createTemporaryLongWaysTable();
		$this->saveDB("_temp_long_ways", $dst);
	}

	private function arrayFrom1() {
		$array = array('');
		unset($array[0]);
		return $array;
	}

	private function pointsDistance($a, $b) {
		$line = new LineString(array($a, $b));
		return $line->length();		
	}

	private function getSourceData() {
		try {
			if ($this->showProgress) $progress = new showProgress("Loading source data from database");
			DB::getPDO()->query("create temporary table `_temp_punkty_adresowe` select * from `{$this->punktyAdresoweTable}` where the_geom is not null");
			DB::getPDO()->query("create temporary table `_temp_drogi` select * from `{$this->drogiTable}` where coalesce(numpoints(the_geom),0)>1");
			DB::getPDO()->query("create index `ID` on `_temp_punkty_adresowe` (`ID`)");
			DB::getPDO()->query("create index `ID` on `_temp_drogi` (`ID`)");
			$this->getSourceData = true;
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function generatePoints() {
		try {
			if (!$this->getSourceData) $this->getSourceData();
			if ($this->showProgress) $progress = new showProgress("Generating points");
			$result = DB::getPDO()->query("select `ID`, astext(`the_geom`) as `point_asText` from `_temp_punkty_adresowe`");
			foreach ($result as $row) $points_asText[$row['ID']] = $row['point_asText'];
			$this->points_asText = $points_asText;
			$this->readyForFindWay = false;
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function createTemporaryWaysTable() {
		try {
			if ((!$this->ways_asText) || (!$this->ways2drogi)) throw new Exception("createTemporaryWaysTable() :: Ways not generated, use generateWays() first");

			DB::getPDO()->query("drop temporary table if exists `_temp_ways`");
			DB::getPDO()->query("create temporary table `_temp_ways` (`ID` int(11), `droga_id` int(11), `the_geom` linestring not null, index(`ID`), index(`droga_id`), spatial index(`the_geom`)) ENGINE=MyISAM");
			if ($this->showProgress) $progress = new showProgress("Creating temporary ways table", count($this->ways_asText));
			foreach ($this->ways_asText as $way_key => $way_asText) {
				DB::getPDO()->query("insert into `_temp_ways` values ('{$way_key}', '{$this->ways2drogi[$way_key]}', LineFromText('{$way_asText}'))");
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->createTemporaryWaysTable = true;
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function generateWaysLenghts() {
		try {
			if (!$this->ways_asText) $this->generateWays;
			if ($this->showProgress) $progress = new showProgress("Generating waysLengths", count($this->ways_asText));
			foreach ($this->ways_asText as $way_key => $way_asText) {
				$way = Vendor_Geophp::load($way_asText, 'wkt');
				$waysLengths[$way_key] = $way->greatCircleLength();
				if ($this->showProgress) $progress->step();
			}
			$this->waysLengths = $waysLengths;
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function generateWays() {
		try {
			if (!$this->getSourceData) $this->getSourceData();

			$ways_asText = $this->arrayFrom1();
			$ways2drogi = $this->arrayFrom1();

			$result = DB::getPDO()->fetchall("select ID as droga_id, astext(the_geom) as multiway from `_temp_drogi`");
			if ($this->showProgress) $progress = new showProgress("Generating ways", count($result));
			foreach ($result as $row) {
				$multiway = Vendor_Geophp::load($row['multiway'], 'wkt');
				for ($i = 1; $i < $n = $multiway->numPoints(); $i++) {
					$way = new LineString(array($multiway->pointN($i), $multiway->pointN($i+1)));
					$ways_asText[] = $way->asText();
					$ways2drogi[] = $row['droga_id'];
					$drogi2ways[$row['droga_id']][] = count($ways2drogi);
				}
				$drogi_asText[$row['droga_id']] = $multiway->asText();
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->ways_asText = $ways_asText;
			$this->ways2drogi = $ways2drogi;
			$this->drogi2ways = $drogi2ways;
			$this->drogi_asText = $drogi_asText;

			$this->joins_asText = array();
			$this->ways2joins = array();
			$this->joins2ways = array();
			$this->xpoints_asText = array();
			$this->points2xpoints = array();
			$this->xpoints2points = array();
			$this->readyForFindWay = false;
			$this->generateWaysLenghts();
		} catch (Exception $e) {
			throw $e;
		}
		
	}

	private function createTemporaryJoinsTable() {
		try {
			if (!$this->joins_asText) throw new Exception("createTemporaryJoinsTable() :: Joins not generated, use generateJoins() first");

			DB::getPDO()->query("drop temporary table if exists `_temp_joins`");
			DB::getPDO()->query("create temporary table `_temp_joins` (`ID` int(11), `the_geom` point not null, index(`ID`), spatial index(`the_geom`)) ENGINE=MyISAM");
			if ($this->showProgress) $progress = new showProgress("Generating temporary joins table", count($this->joins_asText));
			foreach ($this->joins_asText as $join_key => $join_asText) {
				DB::getPDO()->query("insert into `_temp_joins` values ('{$join_key}', PointFromText('{$join_asText}'))");
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();
			$this->createTemporaryJoinsTable = true;
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function generateJoins() {
		try {
			if (!$this->ways_asText) throw new Exception("generateJoins() :: Ways not generated, use generateWays() first");

			$joins_asText = $this->arrayFrom1();
			$ways2joins = array();
			$joins2ways = array();

			if ($this->showProgress) $progress = new showProgress("Generating joins", count($this->ways_asText));
			foreach ($this->ways_asText as $way_key => $way_asText) {
				$way = Vendor_Geophp::load($way_asText, 'wkt');
				$points_asText = array($way->startPoint()->asText(), $way->endPoint()->asText());
				foreach ($points_asText as $point_asText) {
					if (!($join_key=array_search($point_asText, $joins_asText))) $join_key = array_push($joins_asText, $point_asText);
					$ways2joins[$way_key][] = $join_key;
					$joins2ways[$join_key][] = $way_key;						
				}
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->joins_asText = $joins_asText;
			$this->ways2joins = $ways2joins;
			$this->joins2ways = $joins2ways;
			$this->readyForFindWay = false;
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function updateTemporaryPointsTable() {
		try {
			if ((!$this->points2xpoints) || (!$this->xpoints_asText)) throw new Exception("updateTemporaryPointsTable() :: Xpoints not generated, use generateXpoints() first");
			if (!$this->getSourceData) $this->getSourceData();

			if ($this->showProgress) $progress = new showProgress("Updating temporary points table", count($this->points2xpoints));
			foreach ($this->points2xpoints as $point_key => $xpoint_key) {
				DB::getPDO()->query("update `_temp_punkty_adresowe` set `the_geom_xpoint` = pointfromtext('{$this->xpoints_asText[$xpoint_key]}')  where `ID` = '{$point_key}'");
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function generateXpoints() {
		try {
			if (!$this->ways_asText) throw new Exception("generateXpoints() :: Ways not generated, use generateWays() first");
			if (!$this->getSourceData) $this->getSourceData();
			if (!$this->createTemporaryWaysTable) $this->createTemporaryWaysTable();

			$xpoints_asText = $this->arrayFrom1();
			$result = DB::getPDO()->fetchall("select `ID`, astext(`the_geom`) as `point_asText` from `_temp_punkty_adresowe`");
			if ($this->showProgress) $progress = new showProgress("Generating xpoints", count($result));
			foreach ($result as $row) {
				if (!($way_key = DB::getPDO()->fetchValue("select `ID` from (select w.`ID`, st_distance(p.`the_geom`, w.`the_geom`) as `distance` from `_temp_punkty_adresowe` p join `_temp_ways` w on p.`ID` = '{$row['ID']}' order by `distance` limit 1) as ways"))) throw new Exception("generateXpoints() :: błąd spójnosci danych - nie znaleziono najbliższej drogi");
				$way = Vendor_Geophp::load($this->ways_asText[$way_key], 'wkt');

				$a = $way->startPoint();
				$b = $way->endPoint();
				$p = Vendor_Geophp::load($row['point_asText'], 'wkt');

				if ($a->y() == $b->y()) {
					$xp = $p->x();
					$yp = $a->y();
					$deltaA = $p->x() - $a->x();
					$deltaB = $p->x() - $b->x();
				} else {
					if ($a->x() == $b->x()) {
						$xp = $a->x();
						$yp = $p->y();
						$a2 = 0;
						$b2 = $p->y();
					} else {
						$a1 = ($a->y() - $b->y()) / ($a->x() - $b->x());
						$b1 = $a->y() - $a1 * $a->x();

						$a2 = -1 / $a1;
						$b2 = $p->y() - $a2 * $p->x();

						$xp = ($b2 - $b1) / ($a1 - $a2);
						$yp = $a1 * $xp + $b1;
					}
					$deltaA = $a2 * $a->x() + $b2 - $a->y();
					$deltaB = $a2 * $b->x() + $b2 - $b->y();
				}

				if ($deltaA * $deltaB >= 0) { // punkt poza linia, ustalamy na blizszy koniec linii
					if (abs($deltaA) < abs($deltaB) ) $xpoint = $a;
					else $xpoint = $b;
				} else { // punkt w linii - trzeba podzielic linie na dwie
					$xpoint = new Point($xp, $yp);
					$wayA = new LineString(array($a, $xpoint));
					$wayB = new LineString(array($xpoint, $b));
					$this->ways_asText[$way_key] = $wayA->asText();
					$new_way_key = array_push($this->ways_asText, $wayB->asText());
					$this->ways2drogi[$new_way_key] = $this->ways2drogi[$way_key];
					$this->drogi2ways[$this->ways2drogi[$way_key]][] = $new_way_key;
					DB::getPDO()->query("update `_temp_ways` set `the_geom` = linefromtext('{$wayA->asText()}') where `ID` = '{$way_key}'");
					DB::getPDO()->query("insert into `_temp_ways` values ('{$new_way_key}', '{$this->ways2drogi[$way_key]}', linefromtext('{$wayB->asText()}'))");
				}
				if (!($xpoint_key = array_search($xpoint->asText(), $xpoints_asText))) $xpoint_key = array_push($xpoints_asText, $xpoint->asText());
				$points2xpoints[$row['ID']] = $xpoint_key;
				$xpoints2points[$xpoint_key][] = $row['ID'];
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->xpoints_asText = $xpoints_asText;
			$this->points2xpoints = $points2xpoints;
			$this->xpoints2points = $xpoints2points;
			$this->generateJoins();
			$this->updateTemporaryPointsTable();
			$this->generateWaysLenghts();
			$this->readyForFindWay = false;
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function createTemporaryPrzylaczaTable() {
		try {
			if ((!$this->przylacza_asText) || (!$this->przylacza2joins) || (!$this->przylacza2points)) throw new Exception("createTemporaryPrzylaczaTable() :: Przylacza not generated, use generatePrzylacza() first");

			if ($this->showProgress) $progress = new showProgress("Generating temporary table przylacza", count($this->przylacza_asText));
			DB::getPDO()->query("drop temporary table if exists `_temp_przylacza`");
			DB::getPDO()->query("create temporary table `_temp_przylacza` (`ID` int(11), `points_id` int(11), `joins_id` int(11), `the_geom` linestring not null, index(`ID`)) ENGINE=MyISAM");
			foreach ($this->przylacza_asText as $przylacze_key => $przylacze_asText) {
				$query = "insert into `_temp_przylacza` values ('{$przylacze_key}', '{$this->przylacza2points[$przylacze_key]}', '{$this->przylacza2joins[$przylacze_key]}', linefromtext('{$przylacze_asText}'))";
				DB::getPDO()->query($query);
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->createTemporaryPrzylaczaTable = true;
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function generatePrzylacza() {
		try {
			if ((!$this->points2xpoints) || (!$this->xpoints_asText)) throw new Exception("generatePrzylaczaTable() :: Xpoints not generated, use generateXpoints() first");
			if (!$this->joins_asText) throw new Exception("generatePrzylaczaTable() :: Joins not generated, use generateJoins() first");
			if (!$this->points_asText) $this->generatePoints();

			$przylacza_asText = $this->arrayFrom1();
			$przylacza2joins = $this->arrayFrom1();
			$przylacza2points = $this->arrayFrom1();

			if ($this->showProgress) $progress = new showProgress("Generating przylacza", count($this->points_asText));
			foreach ($this->points_asText as $point_key => $point_asText) {
				$xpoint_key = $this->points2xpoints[$point_key];
				$join_key = array_search($this->xpoints_asText[$xpoint_key], $this->joins_asText);
				$point = Vendor_Geophp::load($this->points_asText[$point_key], 'wkt');
				$xpoint = Vendor_Geophp::load($this->xpoints_asText[$xpoint_key], 'wkt');
				$przylacze = new Linestring(array($point, $xpoint));
				$przylacza_asText[] = $przylacze->asText();
				$przylacza2joins[] = $join_key;
				$przylacza2points[] = $point_key;
				$points2joins[$point_key] = $join_key;
				if ($this->showProgress) $progress->step();
			}
			if ($this->showProgress) $progress->stop();

			$this->przylacza_asText = $przylacza_asText;
			$this->przylacza2joins = $przylacza2joins;
			$this->przylacza2points = $przylacza2points;
			$this->points2joins = $points2joins;
			$this->readyForFindWay = false;
		} catch (Exception $e) {
			throw $e;
		}
	}

	private function checkDataForFindWay() {
		try {
			if (!$this->readyForFindWay) {
				if (!$this->ways_asText) {
					$this->generateWays();
					$this->generateXpoints();
				}
				if ((!$this->ways2joins) || (!$this->joins2ways)) $this->generateJoins();
				if (!$this->points_asText) $this->generatePoints();
				if (!$this->points2joins) $this->generatePrzylacza();
				$this->readyForFindWay = true;
			}
		} catch (Exception $e) {
			throw $e;
		}
	}

	public function findWay($joinA, $joinB, $path = array(), $joins = array()) {
		try {
			$this->checkDataForFindWay();
			if ($joinA == $joinB) {
				$joinsPath = array($joinB);
				foreach (array_reverse($path) as $way_key) {
					array_unshift($joinsPath, $this->ways2joins[$way_key][1 - array_search($joinsPath[0], $this->ways2joins[$way_key])]);
				}
				return $joinsPath;
			} else {
				$subPaths = array();
				foreach ($this->joins2ways[$joinA] as $way_key) {
					if (!in_array($way_key, $path)) {
						$new_join = $this->ways2joins[$way_key][1 - array_search($joinA, $this->ways2joins[$way_key])];
						if (!in_array($new_join, $joins)) {
							if ($subPath = $this->findWay($new_join, $joinB, array_merge($path, array($way_key)), array_merge($joins, array($new_join)))) {
								$subPaths[] = $subPath;
							}
						}
					}
				}
				if ($subPaths) {
					if (count($subPaths) == 1) return $subPaths[0];
					else {
						$shortestKey = 0;
						foreach ($subPaths as $key => $subPath) {
							$distance_cur = 0;
							foreach ($subPath as $way_key) {
								$way = Vendor_Geophp::load($this->ways_asText[$way_key], 'wkt');
								if ($way) $distance_cur += Vendor_Geophp::load($this->ways_asText[$way_key], 'wkt')->length();
								else $distance_cur += 999;
							}
							if (!$shortestKey || ($shortestKey && $distance_cur < $distance)) {
								$shortestKey = $key;
								$distance = $distance_cur;
							}
						}
						return $subPaths[$key];
					}
				}
				else {
					return null;
				}
			}
		} catch (Exception $e) {
			throw $e;
		}
	}

	function joins2lineString($joins_keys) {
		try {
			if (!$this->points_asText) $this->generatePoints();
			if ($joins_keys) {
				foreach ($joins_keys as $join_key) {
					$points[] = Vendor_Geophp::load($this->joins_asText[$join_key], 'wkt');
				}
				return new LineString($points);
			} else return null;
		} catch (Exception $e) {
			throw $e;
		}
	}

        private function createTemporaryLongWaysTable() {
                try {
                        if (!$this->longWays_asText) throw new Exception("createTemporaryLongWaysTable() :: longWays not generated, use generateLongWays() first");

                        DB::getPDO()->query("drop temporary table if exists `_temp_long_ways`");
                        DB::getPDO()->query("create temporary table `_temp_long_ways` (`ID` int(11), `the_geom` linestring not null, index(`ID`), spatial index(`the_geom`)) ENGINE=MyISAM");
                        if ($this->showProgress) $progress = new showProgress("Creating temporary longWays table", count($this->longWays_asText));
                        foreach ($this->longWays_asText as $longWay_key => $longWay_asText) {
                                DB::getPDO()->query("insert into `_temp_long_ways` values ('{$longWay_key}', LineFromText('{$longWay_asText}'))");
                                if ($this->showProgress) $progress->step();
                        }
                        if ($this->showProgress) $progress->stop();

                        $this->createTemporaryLongWaysTable = true;
                } catch (Exception $e) {
                        throw $e;
                }
        }

	function generateLongWays() {
		try {
			if ((!$this->drogi2ways) || (!$this->drogi_asText)) $this->generateWays();
			if (!$this->joins_asText) $this->generateJoins();

			if ($this->showProgress) $progress = new showProgress("Generating longWays", count($this->drogi2ways));
			foreach ($this->drogi2ways as $droga_key => $ways_keys) {
				$droga = Vendor_Geophp::load($this->drogi_asText[$droga_key], 'wkt');
				$startPoint = $droga->startPoint();
				$endPoint = $droga->endPoint();
				$start_join_key = array_search($startPoint->asText(), $this->joins_asText);
				$end_join_key = array_search($endPoint->asText(), $this->joins_asText);
				$path = array($start_join_key);
				$last_join_key = $start_join_key;
				while (($last_join_key) != $end_join_key) {
					$next_join_key = 0;
					foreach ($this->joins2ways[$last_join_key] as $way_key) {
						if (in_array($way_key, $ways_keys)) {
							$next_join_key = $this->ways2joins[$way_key][1 - array_search($last_join_key, $this->ways2joins[$way_key])];
							if (!in_array($next_join_key, $path)) {
								$path[] = $next_join_key;
								$last_join_key = $next_join_key;
							}
						}
					}
					if (!$next_join_key) throw new Exception("generateLongWays() :: Błąd spójności danych");
				}
				$points = array();
				foreach ($path as $join_key) $points[] = Vendor_Geophp::load($this->joins_asText[$join_key], 'wkt');
				$longWay = new LineString($points);
				$longWays_asText[$droga_key] = $longWay->asText();
				if ($this->showProgress) $progress->step();
			}
			$this->longWays_asText = $longWays_asText;
			if ($this->showProgress) $progress->stop();
		} catch (Exception $e) {
			throw $e;
		}
	}

}

//$test = new bialePlamy(array("pointsTable"=>"Rozdzielcza_test2_bzyk_punkty_adresowe", "origWaysTable"=>"Rozdzielcza_test2_bzyk_drogi"));
//$test->enableShowProgress();
//$test->loadCache();
//$test->generateWays();
//$test->generateXpoints();
//$test->generatePrzylacza();
//$test->saveCache();
//$test->saveXML();
//$test->saveWaysTable("Rozdzielcza_test2_bzyk_ways");
//$test->saveJoinsTable("Rozdzielcza_test2_bzyk_joins");
//$test->savePrzylaczaTable("Rozdzielcza_test2_bzyk_przylacza");

