From f236bb824829cb2475bbb54e9ddcd8a942fca3ea Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 9 Jun 2010 11:19:24 +0200 Subject: [PATCH] initial import --- .gitignore | 3 + gpsclient.cpp | 67 +++++ gpsclient.h | 52 ++++ main.cpp | 29 ++ mainwidget.cpp | 83 ++++++ mainwidget.h | 53 ++++ mapwidget.cpp | 777 +++++++++++++++++++++++++++++++++++++++++++++++++ mapwidget.h | 99 +++++++ markerlist.cpp | 90 ++++++ markerlist.h | 54 ++++ nanomap.pro | 15 + 11 files changed, 1322 insertions(+) create mode 100644 .gitignore create mode 100644 gpsclient.cpp create mode 100644 gpsclient.h create mode 100644 main.cpp create mode 100644 mainwidget.cpp create mode 100644 mainwidget.h create mode 100644 mapwidget.cpp create mode 100644 mapwidget.h create mode 100644 markerlist.cpp create mode 100644 markerlist.h create mode 100644 nanomap.pro diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82aecb8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.*.swp +build/* +mipsBuild/* diff --git a/gpsclient.cpp b/gpsclient.cpp new file mode 100644 index 0000000..f820523 --- /dev/null +++ b/gpsclient.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "gpsclient.h" + +#include +#include + +GpsClient::GpsClient(QObject *parent) : QObject(parent), + m_socket(new QTcpSocket(this)) +{ + connect(m_socket, SIGNAL(connected()), this, SIGNAL(connected())); + connect(m_socket, SIGNAL(connected()), this, SLOT(conn())); + connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected())); + connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); +} + +void GpsClient::connectGps() +{ + m_socket->connectToHost("127.0.0.1", 2947); +} + +void GpsClient::disconnectGps() +{ + m_socket->disconnectFromHost(); +} + +void GpsClient::query() +{ + QTextStream out(m_socket); + out << "p\n"; +} + +void GpsClient::readData() +{ + QTextStream in(m_socket); + QString reply = in.readLine(); + reply.remove(0, 7); + if (reply != "?") { + float lat = reply.section(' ', 0, 0).toFloat(); + float lon = reply.section(' ', 1, 1).toFloat(); + emit position(QPointF(lon, lat)); + } + QTimer::singleShot(1000, this, SLOT(query())); +} + +void GpsClient::conn() +{ + QTimer::singleShot(1000, this, SLOT(query())); +} + diff --git a/gpsclient.h b/gpsclient.h new file mode 100644 index 0000000..b22e8df --- /dev/null +++ b/gpsclient.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GPSCLIENT_H +#define GPSCLIENT_H + +#include +#include +#include + +class GpsClient : public QObject +{ + Q_OBJECT +public: + GpsClient(QObject *parent = 0); + +public slots: + void connectGps(); + void disconnectGps(); + +signals: + void position(const QPointF &pos); + void connected(); + void disconnected(); + +private slots: + void query(); + void readData(); + void conn(); + +private: + QTcpSocket *m_socket; + +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..17593bd --- /dev/null +++ b/main.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include "mainwidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWidget w; + w.show(); + return a.exec(); +} diff --git a/mainwidget.cpp b/mainwidget.cpp new file mode 100644 index 0000000..14949eb --- /dev/null +++ b/mainwidget.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "mainwidget.h" + +#include "mapwidget.h" +#include "markerlist.h" + +#include + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent), + m_stack(new QStackedWidget(this)), + m_map(new MapWidget(this)), + m_markerList(new MarkerList(this)) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_stack); + + connect(m_map, SIGNAL(switchView()), this, SLOT(showList())); + connect(m_map, SIGNAL(markerAdded(QString)), this, SLOT(markerAdded(QString))); + m_stack->insertWidget(0, m_map); + connect(m_markerList, SIGNAL(back()), this, SLOT(showMap())); + connect(m_markerList, SIGNAL(centerOnMarker(int)), this, SLOT(centerOnMarker(int))); + connect(m_markerList, SIGNAL(removeMarker(int)), this, SLOT(removeMarker(int))); + connect(m_markerList, SIGNAL(markerRenamed(int, QString)), this, SLOT(markerRenamed(int, QString))); + m_stack->insertWidget(1, m_markerList); + + resize(320, 240); +} + +MainWidget::~MainWidget() +{ +} + +void MainWidget::showList() +{ + m_stack->setCurrentIndex(1); +} + +void MainWidget::markerAdded(const QString &name) +{ + m_markerList->addMarker(name); +} + +void MainWidget::showMap() +{ + m_stack->setCurrentIndex(0); +} + +void MainWidget::centerOnMarker(int row) +{ + m_map->centerOnMarker(row); + m_stack->setCurrentIndex(0); +} + +void MainWidget::removeMarker(int row) +{ + m_map->removeMarker(row); +} + +void MainWidget::markerRenamed(int index, const QString &name) +{ + m_map->renameMarker(index, name); +} + diff --git a/mainwidget.h b/mainwidget.h new file mode 100644 index 0000000..7b42964 --- /dev/null +++ b/mainwidget.h @@ -0,0 +1,53 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include +#include +#include + +class MapWidget; +class MarkerList; + +class MainWidget : public QWidget +{ + Q_OBJECT + +public: + MainWidget(QWidget *parent = 0); + ~MainWidget(); + +private slots: + void showList(); + void markerAdded(const QString &name); + void showMap(); + void centerOnMarker(int row); + void removeMarker(int row); + void markerRenamed(int index, const QString &name); + +private: + QStackedWidget *m_stack; + MapWidget *m_map; + MarkerList *m_markerList; + +}; + +#endif // MAINWIDGET_H diff --git a/mapwidget.cpp b/mapwidget.cpp new file mode 100644 index 0000000..c22ef88 --- /dev/null +++ b/mapwidget.cpp @@ -0,0 +1,777 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "mapwidget.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_WS_QWS + #include +#endif +#include +#include +#include +#include + +MapWidget::MapWidget(QWidget *parent) + : QWidget(parent), + m_usage(false), + m_infos(true), + m_zoomable(false), + m_baseName(), + m_xPadding(0), + m_yPadding(0), + m_pos(0, 0), + m_startPos(0, 0), + m_isMoving(false), + m_pixWidth(256), + m_pixHeight(256), + m_pix(), + m_cols(2), + m_rows(2), + m_indexX(0), + m_indexY(0), + m_minIndexX(0), + m_minIndexY(0), + m_maxIndexX(0), + m_maxIndexY(0), + m_minIndexXList(), + m_minIndexYList(), + m_maxIndexXList(), + m_maxIndexYList(), + m_level(0), + m_zoomLevel(), + m_manager(new QNetworkAccessManager(this)), + m_networkMode(false), + m_copyright(), + m_markerPos(), + m_markerName(), + m_drawMarker(true) +{ + for (int x = 0; x < 100; ++x) { + for (int y = 0; y < 100; ++y) { + m_pix[x][y] = 0; + } + } + if (QApplication::arguments().count() > 1) { + loadMapFile(QApplication::arguments().at(1)); + + m_zoomable = m_zoomLevel.count() > 1 && + m_zoomLevel.count() == m_minIndexXList.count() && + m_zoomLevel.count() == m_maxIndexXList.count() && + m_zoomLevel.count() == m_minIndexYList.count() && + m_zoomLevel.count() == m_maxIndexYList.count(); + + m_indexX = (m_minIndexX + m_maxIndexX) / 2; + m_indexY = (m_minIndexY + m_maxIndexY) / 2; + + if (QApplication::arguments().count() > 3) { + bool ok; + int x = QApplication::arguments().at(2).toInt(&ok); + if (ok) { + m_indexX = x; + } + int y = QApplication::arguments().at(3).toInt(&ok); + if (ok) { + m_indexY = y; + } + } + + m_cols = ceil(320.0 / (qreal) m_pixWidth) + 1; + m_rows = ceil(240.0 / (qreal) m_pixHeight) + 1; + + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY+y); + } + } + } else { + m_networkMode = true; + m_zoomable = true; + for (int i = 0; i < 19; ++i) { + m_zoomLevel << QString::number(i); + m_minIndexXList << 0; + m_maxIndexXList << pow(2, i) - 1; + m_minIndexYList << 0; + m_maxIndexYList << pow(2, i) - 1; + } + m_baseName = QDir::homePath()+"/Maps/OSM/%z/%x/%y.png"; + QTimer::singleShot(100, this, SLOT(loadConfig())); + } + + connect(m_manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(replyFinished(QNetworkReply*))); + +#ifdef Q_WS_QWS + QWSServer::setCursorVisible(false); +#endif + setFocusPolicy(Qt::StrongFocus); + resize(320, 240); +} + +MapWidget::~MapWidget() +{ + saveConfig(); +} + +void MapWidget::removeMarker(int index) +{ + if (index >= 0 && m_markerPos.count() > index) { + m_markerPos.removeAt(index); + m_markerName.removeAt(index); + update(); + } +} + +void MapWidget::renameMarker(int index, const QString &name) +{ + if (index >= 0 && m_markerName.count() > index) { + m_markerName.replace(index, name); + } +} + +void MapWidget::centerOnMarker(int index) +{ + if (index >= 0 && m_markerPos.count() > index) { + qreal lon = m_markerPos.at(index).x(); + qreal lat = m_markerPos.at(index).y(); + + centerOnGeoPos(lon, lat); + } +} + +void MapWidget::resizeEvent(QResizeEvent *event) +{ + qreal width = event->size().width(); + qreal height = event->size().height(); + + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + } + } + + m_cols = ceil(width / (qreal) m_pixWidth) + 1; + m_rows = ceil(height / (qreal) m_pixHeight) + 1; + + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY+y); + } + } +} + +void MapWidget::mouseMoveEvent(QMouseEvent *event) +{ + if (m_isMoving) { + m_pos = event->pos() - m_startPos; + updatePos(); + } +} + +void MapWidget::mousePressEvent(QMouseEvent *event) +{ + if (QRect(9, 14, 13, 13).contains(event->pos())) { + changeZoomLevel(1); + reloadPixmaps(); + updatePos(); + m_isMoving = false; + } else if (QRect(9, 214, 13, 13).contains(event->pos())) { + changeZoomLevel(-1); + reloadPixmaps(); + updatePos(); + m_isMoving = false; + } else if (!QRect(5, 10, 20, 220).contains(event->pos())) { + m_startPos = event->pos() - m_pos; + m_isMoving = true; + } +} + +void MapWidget::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + m_isMoving = false; +} + +void MapWidget::wheelEvent(QWheelEvent *event) +{ + if (event->delta() < 0) { + changeZoomLevel(-1); + reloadPixmaps(); + } else { + changeZoomLevel(1); + reloadPixmaps(); + } + updatePos(); +} + +void MapWidget::keyPressEvent(QKeyEvent *event) +{ + int width = 10; + if (event->modifiers() & Qt::AltModifier) { + width = 100; + } + switch (event->key()) { + case Qt::Key_Tab: + { + emit switchView(); + break; + } + case Qt::Key_M: + { + if (event->modifiers() & Qt::AltModifier) { + m_drawMarker = !m_drawMarker; + } else if (event->modifiers() == Qt::NoModifier) { + int n = 0; + if (!m_markerName.isEmpty()) { + n = m_markerName.last().toInt(); + } + QString newName = QString::number(n+1); + m_markerPos << geoPos(); + m_markerName << newName; + emit markerAdded(newName); + } + break; + } + case Qt::Key_K: + case Qt::Key_Up: + { + m_pos += QPoint(0, width); + break; + } + case Qt::Key_J: + case Qt::Key_Down: + { + m_pos += QPoint(0, -width); + break; + } + case Qt::Key_H: + case Qt::Key_Left: + { + m_pos += QPoint(width, 0); + break; + } + case Qt::Key_L: + case Qt::Key_Right: + { + m_pos += QPoint(-width, 0); + break; + } + case Qt::Key_C: + { + m_indexX = (m_minIndexX + m_maxIndexX) / 2; + m_indexY = (m_minIndexY + m_maxIndexY) / 2; + m_pos.setX(m_pixWidth/2); + m_pos.setY(m_pixHeight/2); + reloadPixmaps(); + break; + } + case Qt::Key_O: + { + changeZoomLevel(-1); + reloadPixmaps(); + break; + } + case Qt::Key_I: + { + changeZoomLevel(1); + reloadPixmaps(); + break; + } + case Qt::Key_U: + { + m_infos = !m_infos; + break; + } + case Qt::Key_Q: + { + qApp->quit(); + break; + } + case Qt::Key_Question: + { + m_usage = !m_usage; + break; + } + } + updatePos(); +} + +void MapWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + + bool empty = true; + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + QPixmap *pix = m_pix[x][y]; + if (pix) { + empty = false; + QRect rect(m_pos+QPoint(m_pixWidth*x, m_pixHeight*y), pix->size()); + painter.drawPixmap(rect, *pix); + } + } + } + if (empty) { + painter.drawText(0, 0, width(), height(), Qt::AlignCenter, "No Map Loaded"); + } + if (m_drawMarker) { + int i = 0; + painter.setBrush(QBrush(QColor(255, 237, 60))); + QFontMetrics fm(painter.font()); + int h = fm.height() / 2; + foreach (const QPointF &m, m_markerPos) { + QPoint pos = geo2screen(m.x(), m.y()); + int w = fm.width(m_markerName.at(i)) / 2; + QRect rect(pos.x() - w, pos.y() - h, 2*w, 2*h); + painter.drawRect(rect.adjusted(-2, 0, 2, 0)); + painter.drawText(rect, Qt::AlignCenter, m_markerName.at(i)); + ++i; + } + } + if (m_infos) { + painter.setBrush(QBrush(QColor(255, 255, 255, 210))); + if (m_networkMode) { + painter.drawRoundedRect(30, height() - 20, width() - 35, 19, 10, 10); + QPointF geo = geoPos(); + QString lon = geo.x() > 0 ? QString("E %1").arg(geo.x()) : QString("W %1").arg(-geo.x()); + QString lat = geo.y() > 0 ? QString("N %1").arg(geo.y()) : QString("S %1").arg(-geo.y()); + painter.drawText(35, height() - 18, width() - 45, 15, Qt::AlignCenter, lat+" "+lon); + } + if (m_zoomable) { + painter.drawRoundedRect(5, 10, 20, 220, 10, 10); + painter.setBrush(QBrush(QColor(0, 0, 255, 210))); + int step = 180 / (m_zoomLevel.count() - 1); + for (int i = 0; i < m_zoomLevel.count(); ++i) { + painter.drawLine(5, 211-(i * step), 25, 211-(i * step)); + } + painter.drawRect(9, 208-(m_level * step), 12, 5); + painter.setPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap)); + painter.drawLine(9, 20, 22, 20); + painter.drawLine(15, 14, 15, 27); + painter.drawLine(9, 220, 22, 220); + painter.setPen(QPen()); + } + int midX = width() / 2; + int midY = height() / 2; + painter.drawLine(midX - 5, midY, midX + 5, midY); + painter.drawLine(midX, midY - 5, midX, midY + 5); + } + if (m_usage) { + painter.setBrush(QBrush(QColor(255, 255, 255, 210))); + painter.drawRoundedRect(20, 5, 280, 215, 10, 10); + + QStringList usage; + usage << "q: Quit application"; + usage << "?: Show/hide this message"; + usage << "Arrows: Move map by 10 pixel"; + usage << "Alt+Arrows: Move map by 100 pixel"; + usage << "c: Move to the center of the map"; + if (m_zoomable) { + usage << "i: Zoom in"; + usage << "o: Zoom out"; + } + usage << "u: Show/hide user interface"; + usage << "m: Add a marker"; + usage << "Alt+m: Show/hide all marker"; + usage << "tab: Show/hide marker list"; + if (m_networkMode) { + painter.drawText(30, 200, 260, 20, Qt::AlignCenter, "Map data: (C) OpenStreetMap.org"); + } else if (!m_copyright.isEmpty()) { + painter.drawText(30, 200, 260, 20, Qt::AlignCenter, "Map data: (C) "+m_copyright); + } + painter.drawText(30, 10, 260, 20, Qt::AlignCenter, "NanoMap - Usage"); + painter.drawLine(70, 27, 250, 27); + painter.drawText(30, 30, 260, 200, Qt::AlignLeft, usage.join("\n")); + } + + painter.end(); +} + +void MapWidget::replyFinished(QNetworkReply *reply) +{ + if (reply->error() == QNetworkReply::NoError) { + QString path = reply->url().path(); + int level = path.section('/', 1, 1).toInt(); + int x = path.section('/', 2, 2).toInt(); + QString name = path.section('/', 3, 3); + int y = name.section('.', 0, 0).toInt(); + + QDir base(QDir::homePath()+"/Maps/OSM"); + base.mkpath(QString("%1/%2").arg(level).arg(x)); + + QByteArray data = reply->readAll(); + if (!data.isEmpty()) { + QFile file(QDir::homePath()+"/Maps/OSM"+path); + if (file.open(QFile::WriteOnly)) { + file.write(data); + if (level == m_level) { + if (m_pix[x-m_indexX][y-m_indexY]) { + m_pix[x-m_indexX][y-m_indexY]->loadFromData(data); + } else { + m_pix[x-m_indexX][y-m_indexY] = new QPixmap(file.fileName()); + } + update(); + } + } + } + } + reply->deleteLater(); +} + +void MapWidget::updatePos() +{ + if (m_pos.x() < -m_pixWidth) { + if (m_indexX < m_maxIndexX) { + m_pos.setX(m_pos.x() + m_pixWidth); + m_startPos.setX(m_startPos.x() - m_pixWidth); + ++m_indexX; + + for (int y = 0; y < m_rows; ++y) { + for (int x = 0; x < m_cols; ++x) { + if (x == 0) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + } + if (x < m_cols-1) { + m_pix[x][y] = m_pix[x+1][y]; + } else { + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY+y); + } + } + } + } + } else if (m_pos.x() > 0) { + if (m_indexX > m_minIndexX) { + m_pos.setX(m_pos.x() - m_pixWidth); + m_startPos.setX(m_startPos.x() + m_pixWidth); + --m_indexX; + + for (int y = 0; y < m_rows; ++y) { + for (int x = m_cols-1; x >= 0; --x) { + if (x == m_cols-1) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + } + if (x > 0) { + m_pix[x][y] = m_pix[x-1][y]; + } else { + m_pix[x][y] = loadPixmap(m_indexX, m_indexY+y); + } + } + } + } + } + if (m_pos.y() < -m_pixHeight) { + if (m_indexY < m_maxIndexY) { + m_pos.setY(m_pos.y() + m_pixHeight); + m_startPos.setY(m_startPos.y() - m_pixHeight); + ++m_indexY; + + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + if (y == 0) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + } + if (y < m_rows-1) { + m_pix[x][y] = m_pix[x][y+1]; + } else { + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY+y); + } + } + } + } + } else if (m_pos.y() > 0) { + if (m_indexY > m_minIndexY) { + m_pos.setY(m_pos.y() - m_pixHeight); + m_startPos.setY(m_startPos.y() + m_pixHeight); + --m_indexY; + + for (int x = 0; x < m_cols; ++x) { + for (int y = m_rows-1; y >= 0; --y) { + if (y == m_rows-1) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + } + if (y > 0) { + m_pix[x][y] = m_pix[x][y-1]; + } else { + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY); + } + } + } + } + } + update(); +} + +void MapWidget::reloadPixmaps() +{ + for (int x = 0; x < m_cols; ++x) { + for (int y = 0; y < m_rows; ++y) { + delete m_pix[x][y]; + m_pix[x][y] = 0; + m_pix[x][y] = loadPixmap(m_indexX+x, m_indexY+y); + } + } +} + +QString MapWidget::filename(int x, int y) +{ + QString result; + if (x >= m_minIndexX && x <= m_maxIndexX && + y >= m_minIndexY && y <= m_maxIndexY) { + QString level = QString::number(m_level); + QString sx = QString::number(x); + sx.prepend(QString(m_xPadding-sx.length(), '0')); + QString sy = QString::number(y); + sy.prepend(QString(m_yPadding-sy.length(), '0')); + result = m_baseName; + result.replace("%z", level).replace("%x", sx).replace("%y", sy); + } + return result; +} + +QPixmap* MapWidget::loadPixmap(int x, int y) +{ + QPixmap *pix = 0; + + if (x >= m_minIndexX && x <= m_maxIndexX && + y >= m_minIndexY && y <= m_maxIndexY) { + pix = new QPixmap(filename(x, y)); + if (m_networkMode && pix->isNull()) { + downloadTile(x, y, m_level); + } + } + + return pix; +} + +void MapWidget::loadMapFile(const QString &filename) +{ + QFile file(filename); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QFileInfo info(filename); + m_baseName = info.absolutePath()+QDir::separator(); + + QTextStream in(&file); + QString line; + while (!in.atEnd()) { + line = in.readLine().trimmed(); + if (!line.startsWith('#') && !line.isEmpty()) { + if (line.startsWith("BaseName=")) { + m_baseName.append(line.section('=', 1)); + } else if (line.startsWith("ZeroPadding=")) { + QString tmp = line.section('=', 1); + m_xPadding = tmp.section(',', 0, 0).toInt(); + m_yPadding = tmp.section(',', 1, 1).toInt(); + } else if (line.startsWith("TileWidth=")) { + QString tmp = line.section('=', 1); + m_pixWidth = tmp.toInt(); + } else if (line.startsWith("TileHeight=")) { + QString tmp = line.section('=', 1); + m_pixHeight = tmp.toInt(); + } else if (line.startsWith("ZoomLevel=")) { + QString tmp = line.section('=', 1); + m_zoomLevel << tmp.split(','); + } else if (line.startsWith("MinX=")) { + QString tmp = line.section('=', 1); + QStringList list = tmp.split(','); + foreach (const QString &s, list) { + m_minIndexXList << s.toInt(); + } + m_minIndexX = m_minIndexXList.first(); + } else if (line.startsWith("MaxX=")) { + QString tmp = line.section('=', 1); + QStringList list = tmp.split(','); + foreach (const QString &s, list) { + m_maxIndexXList << s.toInt(); + } + m_maxIndexX = m_maxIndexXList.first(); + } else if (line.startsWith("MinY=")) { + QString tmp = line.section('=', 1); + QStringList list = tmp.split(','); + foreach (const QString &s, list) { + m_minIndexYList << s.toInt(); + } + m_minIndexY = m_minIndexYList.first(); + } else if (line.startsWith("MaxY=")) { + QString tmp = line.section('=', 1); + QStringList list = tmp.split(','); + foreach (const QString &s, list) { + m_maxIndexYList << s.toInt(); + } + m_maxIndexY = m_maxIndexYList.first(); + } else if (line.startsWith("CopyRight=")) { + m_copyright = line.section('=', 1); + } + } + } + } +} + +void MapWidget::loadConfig() +{ + QSettings set(QDir::homePath()+"/.nanomap.conf", QSettings::NativeFormat); + set.beginGroup("map"); + qreal lon = set.value("lon", 0).toReal(); + qreal lat = set.value("lat", 0).toReal(); + int level = set.value("level", 0).toInt(); + m_usage = set.value("usage", true).toBool(); + changeZoomLevel(level - m_level); + centerOnGeoPos(lon, lat); + set.endGroup(); + set.beginGroup("marker"); + QStringList m = set.childKeys(); + foreach (const QString &marker, m) { + QPointF pos = set.value(marker).toPointF(); + m_markerPos << pos; + m_markerName << marker; + emit markerAdded(marker); + } + set.endGroup(); +} + +void MapWidget::saveConfig() +{ + QSettings set(QDir::homePath()+"/.nanomap.conf", QSettings::NativeFormat); + set.beginGroup("map"); + QPointF pos = geoPos(); + set.setValue("lon", pos.x()); + set.setValue("lat", pos.y()); + set.setValue("level", m_level); + set.setValue("usage", m_usage); + set.endGroup(); + set.beginGroup("marker"); + set.remove(""); + for (int i = 0; i < m_markerPos.count(); ++i) { + set.setValue(m_markerName.at(i), m_markerPos.at(i)); + } + set.endGroup(); +} + +void MapWidget::downloadTile(int x, int y, int level) +{ + QUrl url(QString("http://tile.openstreetmap.org/%1/%2/%3.png").arg(level).arg(x).arg(y)); + m_manager->get(QNetworkRequest(url)); +} + +void MapWidget::changeZoomLevel(int diff) +{ + qreal w = width() / 2; + qreal h = height() / 2; + qreal px = (qreal) ((m_indexX - m_minIndexX)*m_pixWidth + w - m_pos.x()) / + (qreal) ((1 + m_maxIndexX - m_minIndexX) * m_pixWidth); + qreal py = (qreal) ((m_indexY - m_minIndexY)*m_pixHeight + h - m_pos.y()) / + (qreal) ((1 + m_maxIndexY - m_minIndexY) * m_pixHeight); + + m_level += diff; + + if (m_level < 0 || m_level >= m_zoomLevel.count()) { + m_level -= diff; + return; + } + + m_minIndexX = m_minIndexXList.at(m_level); + m_maxIndexX = m_maxIndexXList.at(m_level); + m_minIndexY = m_minIndexYList.at(m_level); + m_maxIndexY = m_maxIndexYList.at(m_level); + + qreal x = (px * (1 + m_maxIndexX - m_minIndexX)) + m_minIndexX; + qreal y = (py * (1 + m_maxIndexY - m_minIndexY)) + m_minIndexY; + + m_indexX = (int) floor(x); + m_indexY = (int) floor(y); + m_pos.setX(((m_indexX-x) * m_pixWidth) + w); + m_pos.setY(((m_indexY-y) * m_pixHeight) + h); +} + +void MapWidget::centerOnGeoPos(qreal lon, qreal lat) +{ + qreal w = width() / 2.0; + qreal h = height() / 2.0; + + qreal x = lon2tilex(lon, m_level); + qreal y = lat2tiley(lat, m_level); + + m_indexX = (int) floor(x); + m_indexY = (int) floor(y); + m_pos.setX(((m_indexX-x) * m_pixWidth) + w); + m_pos.setY(((m_indexY-y) * m_pixHeight) + h); + + reloadPixmaps(); + updatePos(); +} + +QPointF MapWidget::geoPos() +{ + qreal w = width() / 2.0; + qreal h = height() / 2.0; + qreal partX = (w - m_pos.x()) / 256.0; + qreal partY = (h - m_pos.y()) / 256.0; + qreal lon = tilex2lon(m_indexX + partX, m_level); + qreal lat = tiley2lat(m_indexY + partY, m_level); + + return QPointF(lon, lat); +} + +QPoint MapWidget::geo2screen(qreal lon, qreal lat) +{ + qreal tx = lon2tilex(lon, m_level); + qreal ty = lat2tiley(lat, m_level); + + int x = (tx * m_pixWidth) - ((m_indexX * m_pixWidth) - m_pos.x()); + int y = (ty * m_pixHeight) - ((m_indexY * m_pixHeight) - m_pos.y()); + + return QPoint(x, y); +} + +qreal MapWidget::lon2tilex(qreal lon, int z) +{ + return (lon + 180.0) / 360.0 * pow(2.0, z); +} + +qreal MapWidget::lat2tiley(qreal lat, int z) +{ + return (1.0 - log(tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z); +} + +qreal MapWidget::tilex2lon(qreal x, int z) +{ + return x / pow(2.0, z) * 360.0 - 180; +} + +qreal MapWidget::tiley2lat(qreal y, int z) +{ + qreal n = M_PI - 2.0 * M_PI * y / pow(2.0, z); + return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); +} + diff --git a/mapwidget.h b/mapwidget.h new file mode 100644 index 0000000..567253a --- /dev/null +++ b/mapwidget.h @@ -0,0 +1,99 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef MAPWIDGET_H +#define MAPWIDGET_H + +#include +#include +#include + +class MapWidget : public QWidget +{ + Q_OBJECT + +public: + MapWidget(QWidget *parent = 0); + ~MapWidget(); + + void removeMarker(int index); + void renameMarker(int index, const QString &name); + +public slots: + void centerOnMarker(int index); + +signals: + void markerAdded(const QString &name); + void switchView(); + +protected: + virtual void resizeEvent(QResizeEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void paintEvent(QPaintEvent *event); + +private slots: + void replyFinished(QNetworkReply *reply); + void loadConfig(); + +private: + void updatePos(); + void reloadPixmaps(); + QString filename(int x, int y); + QPixmap *loadPixmap(int x, int y); + void loadMapFile(const QString &filename); + void saveConfig(); + void downloadTile(int x, int y, int level); + void changeZoomLevel(int diff); + void centerOnGeoPos(qreal lon, qreal lat); + QPointF geoPos(); + QPoint geo2screen(qreal lon, qreal lat); + qreal lon2tilex(qreal lon, int z); + qreal lat2tiley(qreal lat, int z); + qreal tilex2lon(qreal x, int z); + qreal tiley2lat(qreal y, int z); + + bool m_usage, m_infos, m_zoomable; + QString m_baseName; + int m_xPadding, m_yPadding; + QPoint m_pos, m_startPos; + bool m_isMoving; + int m_pixWidth, m_pixHeight; + QPixmap *m_pix[100][100]; + int m_cols, m_rows; + int m_indexX, m_indexY; + int m_minIndexX, m_minIndexY; + int m_maxIndexX, m_maxIndexY; + QList m_minIndexXList, m_minIndexYList; + QList m_maxIndexXList, m_maxIndexYList; + int m_level; + QStringList m_zoomLevel; + QNetworkAccessManager *m_manager; + bool m_networkMode; + QString m_copyright; + QList m_markerPos; + QStringList m_markerName; + bool m_drawMarker; + +}; + +#endif // MAPWIDGET_H diff --git a/markerlist.cpp b/markerlist.cpp new file mode 100644 index 0000000..ad70c28 --- /dev/null +++ b/markerlist.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "markerlist.h" + +#include +#include + +MarkerList::MarkerList(QWidget *parent) + : QWidget(parent), + m_list(new QListWidget(this)), + m_edit(false) +{ + QGridLayout *layout = new QGridLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_list, 0, 0, 1, 3); + m_list->setSelectionMode(QAbstractItemView::SingleSelection); + connect(m_list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(center())); + + QPushButton *back = new QPushButton("Show map", this); + back->setShortcut(QKeySequence(Qt::Key_Tab)); + layout->addWidget(back, 1, 0); + connect(back, SIGNAL(clicked()), this, SIGNAL(back())); + + QPushButton *remove = new QPushButton("&Delete", this); + remove->setShortcut(QKeySequence(Qt::ALT + Qt::Key_D)); + layout->addWidget(remove, 1, 1); + connect(remove, SIGNAL(clicked()), this, SLOT(removeMarker())); + + QPushButton *rename = new QPushButton("&Rename", this); + rename->setShortcut(QKeySequence(Qt::ALT + Qt::Key_R)); + layout->addWidget(rename, 1, 2); + connect(rename, SIGNAL(clicked()), this, SLOT(beginRenameMarker())); + + resize(320, 240); +} + +MarkerList::~MarkerList() +{ +} + +void MarkerList::addMarker(const QString &name) +{ + m_list->addItem(name); +} + +void MarkerList::center() +{ + if (m_edit) { + endRenameMarker(); + } else { + emit centerOnMarker(m_list->currentRow()); + } +} + +void MarkerList::removeMarker() +{ + emit removeMarker(m_list->currentRow()); + m_list->takeItem(m_list->currentRow()); +} + +void MarkerList::beginRenameMarker() +{ + m_edit = true; + m_list->openPersistentEditor(m_list->currentItem()); +} + +void MarkerList::endRenameMarker() +{ + m_edit = false; + m_list->closePersistentEditor(m_list->currentItem()); + emit markerRenamed(m_list->currentRow(), m_list->currentItem()->text()); +} + diff --git a/markerlist.h b/markerlist.h new file mode 100644 index 0000000..dc9479e --- /dev/null +++ b/markerlist.h @@ -0,0 +1,54 @@ +/* + * Copyright 2010 Niels Kummerfeldt + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef MARKERLIST_H +#define MARKERLIST_H + +#include +#include + +class MarkerList : public QWidget +{ + Q_OBJECT + +public: + MarkerList(QWidget *parent = 0); + ~MarkerList(); + + void addMarker(const QString &name); + +signals: + void back(); + void centerOnMarker(int index); + void removeMarker(int index); + void markerRenamed(int index, const QString &name); + +private slots: + void center(); + void removeMarker(); + void beginRenameMarker(); + void endRenameMarker(); + +private: + QListWidget *m_list; + bool m_edit; + +}; + +#endif // MAINWIDGET_H diff --git a/nanomap.pro b/nanomap.pro new file mode 100644 index 0000000..b9b9549 --- /dev/null +++ b/nanomap.pro @@ -0,0 +1,15 @@ +TARGET = NanoMap +TEMPLATE = app + +QT += network + +SOURCES += main.cpp \ + mainwidget.cpp \ + mapwidget.cpp \ + markerlist.cpp \ + gpsclient.cpp + +HEADERS += mainwidget.h \ + mapwidget.h \ + markerlist.h \ + gpsclient.h