#include #include #include #include "Kml.hpp" //#define WITH_FANCY_CAMERA Kml::Kml(const char *pDocRoot) : m_pKml(nullptr) , m_pPlanePlaceMark(nullptr) , m_pTrackPlaceMark(nullptr) , m_pCameraPlaceMark(nullptr) , m_docRoot(pDocRoot) , m_altitudeModeString("absolute") , m_pathColorString("0xff00ffff") , m_cameraAlt(1000) , m_cameraTilt(0) , m_cameraAutoHeading(false) , m_cameraAutoRoll(false) , m_cameraFlyToView(false) { memset(m_path, 0, sizeof(m_path)); } Kml::~Kml(void) { m_pKml = nullptr; } void Kml::updateFlightData(FlightData const &flightData) { const ScopedLock sl (m_lock); m_flightData = flightData; } void Kml::createNetworkLinkFile(String const &path, String const &hostIp) { File file(path); if (file.exists()) { return; } ScopedPointer pKml = new XmlElement("kml"); pKml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2"); XmlElement *pDoc = pKml->createNewChildElement("Document"); pDoc->createNewChildElement("visibility")->addTextElement("1"); XmlElement *pNetworkLink = pDoc->createNewChildElement("NetworkLink"); pNetworkLink->setAttribute("id", "NetworkLink_0"); pNetworkLink->createNewChildElement("name")->addTextElement("Link"); XmlElement *pFlyToView = pNetworkLink->createNewChildElement("flyToView"); if (m_cameraFlyToView) { pFlyToView->addTextElement("1"); } else { pFlyToView->addTextElement("0"); } XmlElement *pLink = pNetworkLink->createNewChildElement("Link"); pLink->createNewChildElement("href")->addTextElement(String("http://") + hostIp + String(":5001/flight.kml")); pLink->createNewChildElement("refreshMode")->addTextElement("onInterval"); pLink->createNewChildElement("refreshInterval")->addTextElement("1"); pLink->createNewChildElement("viewRefreshMode")->addTextElement("onStop"); pLink->createNewChildElement("viewRefreshInterval")->addTextElement("0"); pLink->createNewChildElement("viewFormat")->addTextElement("BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth];CAMERA=[lookatLon],[lookatLat],[lookatRange],[lookatTilt],[lookatHeading];VIEW=[horizFov],[vertFov],[horizPixels],[vertPixels],[terrainEnabled]"); pKml->writeToFile(file, StringRef("")); } const char* Kml::getPath() { return m_path; } void Kml::create() { time_t time_departure; struct tm *time_now; if (m_pKml) destroy(); m_pKml = new XmlElement("kml"); XmlElement *pDoc = m_pKml->createNewChildElement("Document"); time(&time_departure); time_now = localtime(&time_departure); char time_str[256]; // Create an outer XML element.. m_pKml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2"); m_pKml->setAttribute("xmlns:gx", "http://www.google.com/kml/ext/2.2"); sprintf(m_path, "%s\\flight_%4.4d%2.2d%2.2d_%2.2d%2.2d%2.2d.kml", m_docRoot, 1900+time_now->tm_year, time_now->tm_mon+1, time_now->tm_mday, time_now->tm_hour, time_now->tm_min, time_now->tm_sec); pDoc->createNewChildElement("filename")->addTextElement(m_path); sprintf(time_str, "Created on %4.4d-%2.2d-%2.2d at %2.2d:%2.2d:%2.2d", 1900+time_now->tm_year, time_now->tm_mon+1, time_now->tm_mday, time_now->tm_hour, time_now->tm_min, time_now->tm_sec); XmlElement *pSnippet = pDoc->createNewChildElement("Snippet"); pSnippet->setAttribute("maxLines", "2"); pSnippet->addTextElement(time_str); m_pTrackPlaceMark = createTrack(); } XmlElement* Kml::createPlane() { const ScopedLock sl (m_lock); char temp_str[1024]; XmlElement *pPlacemark; XmlElement *pName; XmlElement *pStyle; XmlElement *pIconStyle; XmlElement *pIcon; XmlElement *pHref; XmlElement *pHeading; XmlElement *pY, *pW, *pH; XmlElement *pDescr; XmlElement *pPoint; XmlElement *pExtrude; XmlElement *pTesselate; XmlElement *pAltitudeMode; XmlElement *pCoords; // Placemark pPlacemark = new XmlElement("Placemark"); pPlacemark->setAttribute("id", "Plane_0"); // Name sprintf(temp_str, "Unknown flight"); if (strlen(m_flightData.atcFlightIDStr)) { if (strlen(m_flightData.atcFlightNumStr) == 0) { sprintf(temp_str, "%s", m_flightData.atcFlightIDStr); } else { sprintf(temp_str, "%s/%s", m_flightData.atcFlightIDStr, m_flightData.atcFlightNumStr); } } pName = pPlacemark->createNewChildElement("name"); pName->addTextElement(temp_str); printf("%s\n", temp_str); // Description pDescr = pPlacemark->createNewChildElement("description"); sprintf(temp_str, "%s\nAltitude: %d ft, Speed: %d kts", m_flightData.atcFlightIDStr, (int)(m_flightData.alt_feet+0.5), (int)(m_flightData.gs_knots+0.5)); pDescr->addTextElement(temp_str); // Style pStyle = pPlacemark->createNewChildElement("Style"); // IconStyle pIconStyle = pStyle->createNewChildElement("IconStyle"); pHeading = pIconStyle->createNewChildElement("heading"); pHeading->addTextElement(String(m_flightData.hdgTrue_deg)); // Icon pIcon = pIconStyle->createNewChildElement("Icon"); pHref = pIcon->createNewChildElement("href"); pHref->addTextElement("root://icons/palette-2.png"); pY = pIcon->createNewChildElement("y"); pY->addTextElement("0"); pW = pIcon->createNewChildElement("w"); pW->addTextElement("32"); pH = pIcon->createNewChildElement("h"); pH->addTextElement("32"); // Point pPoint = pPlacemark->createNewChildElement("Point"); pPoint->setAttribute("id", "Pointy"); pExtrude = pPoint->createNewChildElement("extrude"); pExtrude->addTextElement("1"); pTesselate = pPoint->createNewChildElement("tesselate"); pTesselate->addTextElement("1"); pAltitudeMode = pPoint->createNewChildElement("altitudeMode"); pAltitudeMode->addTextElement(m_altitudeModeString); pCoords = pPoint->createNewChildElement("coordinates"); sprintf(temp_str, "%9.6f,%9.6f,%9.6f", m_flightData.lon, m_flightData.lat, m_flightData.alt_meter); pCoords->addTextElement(temp_str); return pPlacemark; } XmlElement* Kml::createNetworkLinkControl() { XmlElement *pNetworkLinkControl = new XmlElement("NetworkLinkControl"); pNetworkLinkControl->createNewChildElement("LinkName")->addTextElement("Link"); XmlElement *pCamera = pNetworkLinkControl->createNewChildElement("Camera"); pCamera->createNewChildElement("longitude")->addTextElement(String(m_flightData.lon)); pCamera->createNewChildElement("latitude")->addTextElement(String(m_flightData.lat)); pCamera->createNewChildElement("altitude")->addTextElement(String(m_flightData.alt_meter+m_cameraAlt)); if (m_cameraAutoHeading) pCamera->createNewChildElement("heading")->addTextElement(String(m_flightData.hdgTrue_deg)); else pCamera->createNewChildElement("heading")->addTextElement("0"); pCamera->createNewChildElement("tilt")->addTextElement(String(m_cameraTilt)); if (m_cameraAutoRoll) pCamera->createNewChildElement("roll")->addTextElement(String(m_flightData.bank_deg)); else pCamera->createNewChildElement("roll")->addTextElement("0"); pCamera->createNewChildElement("altitudeMode")->addTextElement(m_altitudeModeString); #if 0 XmlElement *pUpdate = pNetworkLinkControl->createNewChildElement("Update"); pUpdate->createNewChildElement("targetHref")->addTextElement("FsTrack.kml"); XmlElement *pChange = pUpdate->createNewChildElement("Change"); XmlElement *pNetworkLink = pChange->createNewChildElement("NetworkLink"); pNetworkLink->setAttribute("targetId", "NetworkLink_0"); XmlElement *pFlyToView = pNetworkLink->createNewChildElement("flyToView"); if (m_cameraFlyToView) { pFlyToView->addTextElement("0"); } else { pFlyToView->addTextElement("1"); } #endif return pNetworkLinkControl; } void Kml::destroy() { m_pKml = nullptr; m_pPlanePlaceMark = nullptr; m_pTrackPlaceMark = nullptr; m_pCameraPlaceMark = nullptr; } void Kml::setAltitudeMode(String const &modeString) { m_altitudeModeString = modeString; if (m_pPlanePlaceMark) { m_pPlanePlaceMark->getChildByName("Point")->getChildByName("altitudeMode")->getFirstChildElement()->setText(modeString); } if (m_pTrackPlaceMark) { m_pTrackPlaceMark->getChildByName("LineString")->getChildByName("altitudeMode")->getFirstChildElement()->setText(modeString); } } void Kml::setPathColor(String const &colorString) { const ScopedLock sl (m_lock); m_pathColorString = colorString; if (m_pTrackPlaceMark) { m_pTrackPlaceMark->getChildByName("Style")->getChildByName("LineStyle")->getChildByName("color")->getFirstChildElement()->setText(colorString); } } void Kml::setCameraAltitude(float alt_meters) { m_cameraAlt = alt_meters; } void Kml::setCameraTilt(float tilt_deg) { m_cameraTilt = tilt_deg; } void Kml::setCameraFlyToView(bool enable) { m_cameraFlyToView = enable; } void Kml::setCameraAutoHeading(bool enable) { m_cameraAutoHeading = enable; } void Kml::setCameraAutoRoll(bool enable) { m_cameraAutoRoll = enable; } XmlElement* Kml::createTrack() { const ScopedLock sl (m_lock); char temp_str[1024]; XmlElement *pPlacemark; XmlElement *pName; XmlElement *pStyle; XmlElement *pLineStyle; XmlElement *pLineColor; XmlElement *pLineWidth; XmlElement *pPolyStyle; XmlElement *pPolyColor; XmlElement *pLineString; XmlElement *pExtrude; XmlElement *pTesselate; XmlElement *pAltitudeMode; XmlElement *pCoords; // Placemark pPlacemark = new XmlElement("Placemark"); pPlacemark->setAttribute("id", "Track_0"); // Name pName = pPlacemark->createNewChildElement("name"); pName->addTextElement("Track"); // Style pStyle = pPlacemark->createNewChildElement("Style"); // LineStyle pLineStyle = pStyle->createNewChildElement("LineStyle"); pLineColor = pLineStyle->createNewChildElement("color"); pLineColor->addTextElement(m_pathColorString); pLineWidth = pLineStyle->createNewChildElement("width"); pLineWidth->addTextElement("4"); // PolyStyle pPolyStyle = pStyle->createNewChildElement("PolyStyle"); pPolyColor = pPolyStyle->createNewChildElement("color"); sprintf(temp_str, "%4.4X", 0x7f00ff00); pPolyColor->addTextElement(temp_str); // LineString pLineString = pPlacemark->createNewChildElement("LineString"); pExtrude = pLineString->createNewChildElement("extrude"); pExtrude->addTextElement("1"); pTesselate = pLineString->createNewChildElement("tesselate"); pTesselate->addTextElement("1"); pAltitudeMode = pLineString->createNewChildElement("altitudeMode"); pAltitudeMode->addTextElement(m_altitudeModeString); sprintf(temp_str, "\n%9.6f,%9.6f,%9.6f", m_flightData.lon, m_flightData.lat, m_flightData.alt_meter); pCoords = pLineString->createNewChildElement("coordinates"); pCoords = pPlacemark->getChildByName("LineString")->getChildByName("coordinates"); pCoords->addTextElement(temp_str); return pPlacemark; } void Kml::updateTrack() { const ScopedLock sl (m_lock); char temp_str[1024]; if (!m_pTrackPlaceMark) { return; } // Coordinates sprintf(temp_str, "\n%9.6f,%9.6f,%9.6f", m_flightData.lon, m_flightData.lat, m_flightData.alt_meter); XmlElement *pCoords = m_pTrackPlaceMark->getChildByName("LineString")->getChildByName("coordinates"); pCoords->addTextElement(temp_str); } void Kml::exportKml(const char *pPath) { const ScopedLock sl (m_lock); if (pPath && m_pKml) { File file(pPath); ScopedPointer pPlane = createPlane(); ScopedPointer pNetworkLinkcontrol = createNetworkLinkControl(); m_pKml->addChildElement(pNetworkLinkcontrol); m_pKml->getChildByName("Document")->addChildElement(pPlane); m_pKml->getChildByName("Document")->addChildElement(m_pTrackPlaceMark); m_pKml->writeToFile(file, StringRef("")); m_pKml->removeChildElement(pNetworkLinkcontrol, false); m_pKml->getChildByName("Document")->removeChildElement(pPlane, false); m_pKml->getChildByName("Document")->removeChildElement(m_pTrackPlaceMark, false); } } void Kml::exportKmz(const char *pPath) { // ToDo: add Compression if available in Juce const ScopedLock sl (m_lock); if (pPath && m_pKml) { File file(pPath); file.deleteFile(); ScopedPointer pStream = file.createOutputStream(); m_pKml->writeToStream(*pStream, StringRef("")); pStream = nullptr; } } void Kml::onUpdate(const char *pPath, ViewFormat const &viewFormat) { if (m_pKml == nullptr) return; // LookAt // XmlElement *pCamera; // pCamera = m_pKml->getChildByName("NetworkLinkControl")->getChildByName("Camera"); if (viewFormat.has_camera) { // pCamera->getChildByName("longitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatLon)); // pCamera->getChildByName("latitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatLat)); // pCamera->getChildByName("altitude")->getFirstChildElement()->setText(String(viewFormat.camera.lookatRange)); // pCamera->getChildByName("heading")->getFirstChildElement()->setText(String(viewFormat.camera.lookatHeading)); // pCamera->getChildByName("heading")->getFirstChildElement()->setText(String(0)); // pCamera->getChildByName("tilt")->getFirstChildElement()->setText(String(viewFormat.camera.lookatTilt)); // pCamera->getChildByName("tilt")->getFirstChildElement()->setText(String(0)); // pCamera->getChildByName("range")->getFirstChildElement()->setText(String(viewFormat.camera.lookatRange)); // pCamera->getChildByName("range")->getFirstChildElement()->setText(String("10000")); // pCamera->getChildByName("altitudeMode")->getFirstChildElement()->setText(m_altitudeModeString); } exportKml(pPath); }