_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[] = 'Track ' . $station->name . ''; if (!empty($packets)) { $firstPacket = $packets[0]; $lastPacket = $packets[count($packets) - 1]; $lastPacketTime = new DateTime(); $lastPacketTime->setTimestamp($lastPacket->timestamp); $description[] = ''; if (count($packets) == 1) { $description[] = '' . $lastPacketTime->format('Y-m-d H:i:s e') . ''; } else { $firstPacketTime = new DateTime(); $firstPacketTime->setTimestamp($firstPacket->timestamp); $description[] = '' . $firstPacketTime->format('Y-m-d H:i:s e') . ' -
' . $lastPacketTime->format('Y-m-d H:i:s e') . '
'; } if ($lastPacket->comment != null) { $description[] = ''; $description[] = '' . htmlspecialchars($lastPacket->comment) . ''; } if ($lastPacket->speed !== null) { $description[] = ''; $description[] = 'Latest speed: ' . $lastPacket->speed . 'kmh' . ''; if ($lastPacket->altitude !== null) { $description[] = 'Latest altitude: ' . $lastPacket->altitude . 'm ' . ''; } if ($lastPacket->course !== null) { $description[] = 'Latest course: ' . $lastPacket->course . '°' . ''; } } } return '
' . implode('
', $description) . '
'; } 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'] ); } }