commit f236bb824829cb2475bbb54e9ddcd8a942fca3ea Author: Niels Date: Wed Jun 9 11:19:24 2010 +0200 initial import 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