309 lines
10 KiB
PHP
309 lines
10 KiB
PHP
<?php
|
|
|
|
class Kml {
|
|
|
|
private $_class;
|
|
private static $_singletonInstance = null;
|
|
|
|
public function __construct($class = 'Kml')
|
|
{
|
|
$this->_class = $class;
|
|
}
|
|
|
|
public static function getInstance()
|
|
{
|
|
if (self::$_singletonInstance === null) {
|
|
self::$_singletonInstance = new Kml();
|
|
}
|
|
|
|
return self::$_singletonInstance;
|
|
}
|
|
|
|
public function getKmlDomDocument($station, $packets, $startTimestamp, $endTimestamp, $color)
|
|
{
|
|
$dom = new DOMDocument('1.0', 'UTF-8');
|
|
$node = $dom->createElementNS('http://earth.google.com/kml/2.2', 'kml');
|
|
$parNode = $dom->appendChild($node);
|
|
|
|
$dnode = $dom->createElement('Document');
|
|
$docNode = $parNode->appendChild($dnode);
|
|
|
|
if (!empty($packets)) {
|
|
$lastPacket = $packets[count($packets) - 1];
|
|
$makrerStyleNode = $this->getMarkerStyleNode($dom, $lastPacket, $color);
|
|
$docNode->appendChild($makrerStyleNode);
|
|
|
|
$folderNode = $dom->createElement('Folder');
|
|
$docNode->appendChild($folderNode);
|
|
|
|
$nameNode = $dom->createElement('name', $station->name
|
|
. ' (' . $startTimestamp . ' - ' . $endTimestamp . ')');
|
|
$folderNode->appendChild($nameNode);
|
|
|
|
$stationNode = $this->getStationNode($dom, $station, $packets);
|
|
$folderNode->appendChild($stationNode);
|
|
}
|
|
return $dom;
|
|
}
|
|
|
|
public function formatKmlContent($kmlContent, $tab="\t", $tabCount = 1)
|
|
{
|
|
$kmlContent = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $kmlContent);
|
|
|
|
$token = strtok($kmlContent, "\n");
|
|
$result = '';
|
|
$pad = 0;
|
|
$matches = array();
|
|
|
|
while ($token !== false)
|
|
{
|
|
$token = trim($token);
|
|
if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) $indent=0;
|
|
elseif (preg_match('/^<\/\w/', $token, $matches))
|
|
{
|
|
$pad--;
|
|
if($indent>0) $indent=0;
|
|
}
|
|
elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches))
|
|
{
|
|
$indent=1;
|
|
}
|
|
else $indent = 0;
|
|
|
|
$line = str_pad($token, strlen($token)+($pad*$tabCount), $tab, STR_PAD_LEFT);
|
|
$result .= $line."\n";
|
|
$token = strtok("\n");
|
|
$pad += $indent;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function getMarkerStyleNode($dom, $packet, $color)
|
|
{
|
|
$styleNode = $dom->createElement('Style');
|
|
$styleNode->setAttribute('id', 'markerStyle' . $packet->stationId);
|
|
|
|
$iconstyleNode = $dom->createElement('IconStyle');
|
|
$iconNode = $dom->createElement('Icon');
|
|
$href = $dom->createElement('href', $this->getWebsiteURL() . $packet->getIconFilePath(null, null, true));
|
|
$iconNode->appendChild($href);
|
|
$iconstyleNode->appendChild($iconNode);
|
|
$styleNode->appendChild($iconstyleNode);
|
|
|
|
$linestyleNode = $dom->createElement('LineStyle');
|
|
|
|
if (!is_null($color) && preg_match('/^[A-F0-9]{8}$/', $color)) {
|
|
$colorNode = $dom->createElement('color', $color);
|
|
$linestyleNode->appendChild($colorNode);
|
|
} else {
|
|
$colorNode = $dom->createElement('color', 'ff55ff55');
|
|
$linestyleNode->appendChild($colorNode);
|
|
}
|
|
|
|
$widthNode = $dom->createElement('width', '5');
|
|
$linestyleNode->appendChild($widthNode);
|
|
$styleNode->appendChild($linestyleNode);
|
|
|
|
return $styleNode;
|
|
}
|
|
|
|
private function getStationNode($dom, $station, $packets) {
|
|
$lastPacket = $packets[count($packets) - 1];
|
|
|
|
$placeNode = $dom->createElement('Placemark');
|
|
$placeNode->setAttribute('id', 'station-' . $station->getId());
|
|
$styleUrl = $dom->createElement('styleUrl', '#markerStyle' . $station->getId());
|
|
$placeNode->appendChild($styleUrl);
|
|
|
|
$nameNode = $dom->createElement('name', htmlspecialchars($station->name));
|
|
$placeNode->appendChild($nameNode);
|
|
|
|
$description = $this->getPacketNodeDescription($station, $packets);
|
|
if ($description !== null) {
|
|
$node = $dom->createElement('description', ($description));
|
|
$placeNode->appendChild($node);
|
|
}
|
|
|
|
$multiGeometryNode = $this->getMultiGeometryNode($dom, $station, $packets);
|
|
$placeNode->appendChild($multiGeometryNode);
|
|
|
|
return $placeNode;
|
|
}
|
|
|
|
private function getMultiGeometryNode($dom, $station, $packets) {
|
|
$lastPacket = $packets[count($packets) - 1];
|
|
|
|
$multiGeometryNode = $dom->createElement('MultiGeometry');
|
|
|
|
$altitudeMode = 'clampToGround';
|
|
if ($this->isFlyingObject($station, $packets)) {
|
|
$altitudeMode = 'absolute';
|
|
}
|
|
|
|
$pointNode = $this->getPacketPointNode($dom, $lastPacket, $altitudeMode);
|
|
$multiGeometryNode->appendChild($pointNode);
|
|
|
|
if (count($packets) > 1) {
|
|
$tailLineNode = $this->getTailLineNode($dom, $packets, $altitudeMode);
|
|
$multiGeometryNode->appendChild($tailLineNode);
|
|
}
|
|
|
|
return $multiGeometryNode;
|
|
}
|
|
|
|
private function getPacketPointNode($dom, $packet, $altitudeMode)
|
|
{
|
|
$pointNode = $dom->createElement('Point');
|
|
|
|
// Creates a coordinates element and gives it the value of the lng and lat columns from the results.
|
|
if ($packet->altitude !== null) {
|
|
$coorStr = round($packet->longitude, 5) . ',' . round($packet->latitude, 5) . ',' . round($packet->altitude, 2);
|
|
} else {
|
|
$coorStr = round($packet->longitude, 5) . ',' . round($packet->latitude, 5);
|
|
}
|
|
|
|
$altitudeModeNode = $dom->createElement('altitudeMode', $altitudeMode);
|
|
$pointNode->appendChild($altitudeModeNode);
|
|
|
|
$coorNode = $dom->createElement('coordinates', $coorStr);
|
|
$pointNode->appendChild($coorNode);
|
|
return $pointNode;
|
|
}
|
|
|
|
private function getTailLineNode($dom, $packets, $altitudeMode)
|
|
{
|
|
$lineStringNode = $dom->createElement('LineString');
|
|
|
|
$lineCoorStr = '';
|
|
foreach ($packets as $packet) {
|
|
if ($packet->altitude !== null) {
|
|
$lineCoorStr .= ' ' . round($packet->longitude, 5) . ',' . round($packet->latitude, 5) . ',' . round($packet->altitude, 2);
|
|
} else {
|
|
$lineCoorStr .= ' ' . round($packet->longitude, 5) . ',' . round($packet->latitude, 5);
|
|
}
|
|
}
|
|
|
|
$altitudeModeNode = $dom->createElement('altitudeMode', $altitudeMode);
|
|
$lineStringNode->appendChild($altitudeModeNode);
|
|
|
|
$tessellateNode = $dom->createElement('tessellate', '1');
|
|
$lineStringNode->appendChild($tessellateNode);
|
|
|
|
$extrudeNode = $dom->createElement('extrude', '1');
|
|
$lineStringNode->appendChild($extrudeNode);
|
|
|
|
$lineCoorNode = $dom->createElement('coordinates', $lineCoorStr);
|
|
$lineStringNode->appendChild($lineCoorNode);
|
|
|
|
return $lineStringNode;
|
|
}
|
|
|
|
private function getPacketNodeDescription($station, $packets)
|
|
{
|
|
$description = array();
|
|
$description[] = '<a href="' . $this->getWebsiteURL() . '?sid=' . $station->getId() . '" target="_blank">Track ' . $station->name . '</a>';
|
|
|
|
if (!empty($packets)) {
|
|
$firstPacket = $packets[0];
|
|
$lastPacket = $packets[count($packets) - 1];
|
|
|
|
$lastPacketTime = new DateTime();
|
|
$lastPacketTime->setTimestamp($lastPacket->timestamp);
|
|
|
|
$description[] = '';
|
|
if (count($packets) == 1) {
|
|
$description[] = '<span style="color: grey;">' . $lastPacketTime->format('Y-m-d H:i:s e') . '</span>';
|
|
} else {
|
|
$firstPacketTime = new DateTime();
|
|
$firstPacketTime->setTimestamp($firstPacket->timestamp);
|
|
|
|
$description[] = '<span style="color: grey;">' . $firstPacketTime->format('Y-m-d H:i:s e') . ' -<br>' . $lastPacketTime->format('Y-m-d H:i:s e') . '</span>';
|
|
}
|
|
|
|
if ($lastPacket->comment != null) {
|
|
$description[] = '';
|
|
$description[] = '<span style="font-style: italic; color: #754D08;">' . htmlspecialchars($lastPacket->comment) . '</span>';
|
|
}
|
|
|
|
if ($lastPacket->speed !== null) {
|
|
$description[] = '';
|
|
$description[] = 'Latest speed: <span style="color: blue;">' . $lastPacket->speed . 'kmh' . '</span>';
|
|
|
|
if ($lastPacket->altitude !== null) {
|
|
$description[] = 'Latest altitude: <span style="color: blue;">' . $lastPacket->altitude . 'm ' . '</span>';
|
|
}
|
|
|
|
if ($lastPacket->course !== null) {
|
|
$description[] = 'Latest course: <span style="color: blue;">' . $lastPacket->course . '°' . '</span>';
|
|
}
|
|
}
|
|
}
|
|
|
|
return '<div style="margin-right: 45px;">' . implode('<br/>', $description) . '</div>';
|
|
}
|
|
|
|
private function isClientRequestingToOften()
|
|
{
|
|
$currentMinuteTs = (time() - (time() % 60));
|
|
$limitedNumberOfRequestsPerMinute = 60;
|
|
$logFile = 'tmp/export_kml_ip_' . $_SERVER['REMOTE_ADDR'] . '_' . $currentMinuteTs;
|
|
|
|
// Create dir if missing
|
|
if (!file_exists('tmp')) {
|
|
mkdir('tmp');
|
|
}
|
|
|
|
// Delete old log-files
|
|
$files = glob('tmp/*');
|
|
foreach ($files as $file) {
|
|
if (is_file($file) && !stristr($file, '_' . $currentMinuteTs)) {
|
|
unlink($file);
|
|
}
|
|
}
|
|
|
|
// Log request
|
|
$log = fopen($logFile, "a");
|
|
fwrite($log,'0');
|
|
fclose($log);
|
|
|
|
// Check limit
|
|
if (filesize($logFile) > $limitedNumberOfRequestsPerMinute) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function isFlyingObject($station, $packets)
|
|
{
|
|
if (count($packets) <= 1) {
|
|
// A station with only one packet is probably stationary
|
|
return false;
|
|
}
|
|
|
|
if ($station->sourceId == 4) {
|
|
// All gliderradar stations with a tail is probably flying
|
|
return true;
|
|
}
|
|
|
|
foreach ($packets as $packet) {
|
|
if ($packet->altitude !== null && $packet->altitude > 1000) {
|
|
// If altitude is above 1000m it is probably flying
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function getWebsiteURL()
|
|
{
|
|
return sprintf(
|
|
"%s://%s",
|
|
isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http',
|
|
$_SERVER['SERVER_NAME']
|
|
);
|
|
}
|
|
}
|