/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2021, 2023 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation as version 3 of the License, or // // (at your option) any later version. // // // // This program is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY; without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License V3 for more details. // // // // You should have received a copy of the GNU General Public License // // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// #ifndef INCLUDE_OSMTEMPLATE_SERVER_H_ #define INCLUDE_OSMTEMPLATE_SERVER_H_ #include #include #include #include class OSMTemplateServer : public QTcpServer { Q_OBJECT private: QString m_thunderforestAPIKey; QString m_maptilerAPIKey; quint16 m_tileServerPort; bool m_overlay; public: // port - port to listen on / is listening on. Use 0 for any free port. OSMTemplateServer(const QString &thunderforestAPIKey, const QString &maptilerAPIKey, quint16 tileServerPort, quint16 &port, QObject* parent = 0) : QTcpServer(parent), m_thunderforestAPIKey(thunderforestAPIKey), m_maptilerAPIKey(maptilerAPIKey), m_tileServerPort(tileServerPort), m_overlay(false) { listen(QHostAddress::Any, port); port = serverPort(); } void incomingConnection(qintptr socket) override { QTcpSocket* s = new QTcpSocket(this); connect(s, SIGNAL(readyRead()), this, SLOT(readClient())); connect(s, SIGNAL(disconnected()), this, SLOT(discardClient())); s->setSocketDescriptor(socket); //addPendingConnection(socket); } void setThunderforestAPIKey(const QString& thunderforestAPIKey) { m_thunderforestAPIKey = thunderforestAPIKey; } void setMaptilerAPIKey(const QString& maptilerAPIKey) { m_maptilerAPIKey = maptilerAPIKey; } void setEnableOverlay(bool enableOverlay) { m_overlay = enableOverlay; } private slots: void readClient() { QTcpSocket* socket = (QTcpSocket*)sender(); if (socket->canReadLine()) { QString line = socket->readLine(); qDebug() << "HTTP Request: " << line; QStringList tokens = QString(line).split(QRegularExpression("[ \r\n][ \r\n]*")); if (tokens[0] == "GET") { bool hires = tokens[1].contains("hires"); QString hiresURL = hires ? "@2x" : ""; QString xml, url; if ((tokens[1] == "/street") || (tokens[1] == "/street-hires")) { if (m_overlay) { url = QString("http://127.0.0.1:%1/street/%z/%x/%y.png").arg(m_tileServerPort); } else { url = "https://tile.openstreetmap.org/%z/%x/%y.png"; } xml = QString("\ {\ \"UrlTemplate\" : \"%1\",\ \"ImageFormat\" : \"png\",\ \"QImageFormat\" : \"Indexed8\",\ \"ID\" : \"wmf-intl-1x\",\ \"MaximumZoomLevel\" : 19,\ \"MapCopyRight\" : \"OpenStreetMap\",\ \"DataCopyRight\" : \"\"\ }").arg(url); } else if (tokens[1] == "/satellite") { if (m_overlay) { url = QString("http://127.0.0.1:%1/satellite/%z/%x/%y.jpg").arg(m_tileServerPort); } else { url = QString("https://api.maptiler.com/tiles/satellite-v2/%z/%x/%y%1.jpg?key=%2").arg(hiresURL).arg(m_maptilerAPIKey); } xml = QString("\ {\ \"Enabled\" : true,\ \"UrlTemplate\" : \"%1\",\ \"ImageFormat\" : \"jpg\",\ \"QImageFormat\" : \"RGB888\",\ \"ID\" : \"usgs-l7\",\ \"MaximumZoomLevel\" : 22,\ \"MapCopyRight\" : \"Maptiler\",\ \"DataCopyRight\" : \"\"\ }").arg(url); } else if (tokens[1].contains("transit")) { QStringList map({"/night-transit", "/night-transit-hires", "/transit", "/transit-hires"}); QStringList mapId({"thf-nighttransit", "thf-nighttransit-hires", "thf-transit", "thf-transit-hires"}); QStringList mapUrl({"dark_nolabels", "dark_nolabels", "light_nolabels", "light_nolabels"}); // Use CartoDB maps without labels for aviation maps int idx = map.indexOf(tokens[1]); if (m_overlay) { url = QString("http://127.0.0.1:%1/%2/%z/%x/%y.png").arg(m_tileServerPort).arg(mapUrl[idx]); } else { url = QString("http://1.basemaps.cartocdn.com/%2/%z/%x/%y.png%1").arg(hiresURL).arg(mapUrl[idx]); } xml = QString("\ {\ \"UrlTemplate\" : \"%1\",\ \"ImageFormat\" : \"png\",\ \"QImageFormat\" : \"Indexed8\",\ \"ID\" : \"%2\",\ \"MaximumZoomLevel\" : 20,\ \"MapCopyRight\" : \"CartoDB\",\ \"DataCopyRight\" : \"\"\ }").arg(url).arg(mapId[idx]); } else { QStringList map({"/cycle", "/cycle-hires", "/hiking", "/hiking-hires", "/night-transit", "/night-transit-hires", "/terrain", "/terrain-hires", "/transit", "/transit-hires"}); QStringList mapId({"thf-cycle", "thf-cycle-hires", "thf-hike", "thf-hike-hires", "thf-nighttransit", "thf-nighttransit-hires", "thf-landsc", "thf-landsc-hires", "thf-transit", "thf-transit-hires"}); QStringList mapUrl({"cycle", "cycle", "outdoors", "outdoors", "transport-dark", "transport-dark", "landscape", "landscape", "transport", "transport"}); int idx = map.indexOf(tokens[1]); if (idx != -1) { if (m_overlay) { url = QString("http://127.0.0.1:%1/%2/%z/%x/%y.png").arg(m_tileServerPort).arg(mapUrl[idx]); } else { url = QString("http://a.tile.thunderforest.com/%1/%z/%x/%y%3.png?apikey=%2").arg(mapUrl[idx]).arg(m_thunderforestAPIKey).arg(hiresURL); } xml = QString("\ {\ \"UrlTemplate\" : \"%1\",\ \"ImageFormat\" : \"png\",\ \"QImageFormat\" : \"Indexed8\",\ \"ID\" : \"%2\",\ \"MaximumZoomLevel\" : 20,\ \"MapCopyRight\" : \"Thunderforest\",\ \"DataCopyRight\" : \"OpenStreetMap contributors\"\ }").arg(url).arg(mapId[idx]); } } QTextStream os(socket); os.setAutoDetectUnicode(true); os << "HTTP/1.0 200 Ok\r\n" "Content-Type: text/html; charset=\"utf-8\"\r\n" "\r\n" << xml << "\n"; socket->close(); if (socket->state() == QTcpSocket::UnconnectedState) { delete socket; } } } } void discardClient() { QTcpSocket* socket = (QTcpSocket*)sender(); socket->deleteLater(); } }; #endif