/** ****************************************************************************** * * @file core.cpp * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. * @brief * @see The GNU Public License (GPL) Version 3 * @defgroup OPMapWidget * @{ * *****************************************************************************/ /* * 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 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 * 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., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "core.h" #ifdef DEBUG_CORE qlonglong internals::Core::debugcounter = 0; #endif using namespace projections; namespace internals { Core::Core() : MouseWheelZooming(false), currentPosition(0, 0), currentPositionPixel(0, 0), LastLocationInBounds(-1, -1), sizeOfMapArea(0, 0) , minOfTiles(0, 0), maxOfTiles(0, 0), zoom(0), isDragging(false), TooltipTextPadding(10, 10), loaderLimit(5), maxzoom(21), runningThreads(0), started(false) { mousewheelzoomtype = MouseWheelZoomType::MousePositionAndCenter; SetProjection(new MercatorProjection()); this->setAutoDelete(false); ProcessLoadTaskCallback.setMaxThreadCount(10); renderOffset = Point(0, 0); dragPoint = Point(0, 0); CanDragMap = true; tilesToload = 0; OPMaps::Instance(); } Core::~Core() { ProcessLoadTaskCallback.waitForDone(); } void Core::run() { MrunningThreads.lock(); ++runningThreads; MrunningThreads.unlock(); #ifdef DEBUG_CORE qlonglong debug; Mdebug.lock(); debug = ++debugcounter; Mdebug.unlock(); qDebug() << "core:run" << " ID=" << debug; #endif // DEBUG_CORE bool last = false; LoadTask task; MtileLoadQueue.lock(); { if (tileLoadQueue.count() > 0) { task = tileLoadQueue.dequeue(); { last = (tileLoadQueue.count() == 0); #ifdef DEBUG_CORE qDebug() << "TileLoadQueue: " << tileLoadQueue.count() << " Point:" << task.Pos.ToString() << " ID=" << debug;; #endif // DEBUG_CORE } } } MtileLoadQueue.unlock(); if (task.HasValue()) { if (loaderLimit.tryAcquire(1, OPMaps::Instance()->Timeout)) { MtileToload.lock(); --tilesToload; MtileToload.unlock(); #ifdef DEBUG_CORE qDebug() << "loadLimit semaphore aquired " << loaderLimit.available() << " ID=" << debug << " TASK=" << task.Pos.ToString() << " " << task.Zoom; #endif // DEBUG_CORE { #ifdef DEBUG_CORE qDebug() << "task as value, begining get" << " ID=" << debug;; #endif // DEBUG_CORE { Tile *m = Matrix.TileAt(task.Pos); if (m == 0 || m->Overlays.count() == 0) { #ifdef DEBUG_CORE qDebug() << "Fill empty TileMatrix: " + task.ToString() << " ID=" << debug;; #endif // DEBUG_CORE Tile *t = new Tile(task.Zoom, task.Pos); QVector layers = OPMaps::Instance()->GetAllLayersOfType(GetMapType()); foreach(MapType::Types tl, layers) { int retry = 0; do { QByteArray img; // tile number inversion(BottomLeft -> TopLeft) for pergo maps if (tl == MapType::PergoTurkeyMap) { img = OPMaps::Instance()->GetImageFrom(tl, Point(task.Pos.X(), maxOfTiles.Height() - task.Pos.Y()), task.Zoom); } else { // ok #ifdef DEBUG_CORE qDebug() << "start getting image" << " ID=" << debug; #endif // DEBUG_CORE img = OPMaps::Instance()->GetImageFrom(tl, task.Pos, task.Zoom); #ifdef DEBUG_CORE qDebug() << "Core::run:gotimage size:" << img.count() << " ID=" << debug << " time=" << t.elapsed(); #endif // DEBUG_CORE } if (img.length() != 0) { Moverlays.lock(); { t->Overlays.append(img); #ifdef DEBUG_CORE qDebug() << "Core::run append img:" << img.length() << " to tile:" << t->GetPos().ToString() << " now has " << t->Overlays.count() << " overlays" << " ID=" << debug; #endif // DEBUG_CORE } Moverlays.unlock(); break; } else if (OPMaps::Instance()->RetryLoadTile > 0) { #ifdef DEBUG_CORE qDebug() << "ProcessLoadTask: " << task.ToString() << " -> empty tile, retry " << retry << " ID=" << debug;; #endif // DEBUG_CORE { QWaitCondition wait; QMutex m; m.lock(); wait.wait(&m, 500); } } } while (++retry < OPMaps::Instance()->RetryLoadTile); } if (t->Overlays.count() > 0) { Matrix.SetTileAt(task.Pos, t); emit OnNeedInvalidation(); #ifdef DEBUG_CORE qDebug() << "Core::run add tile " << t->GetPos().ToString() << " to matrix index " << task.Pos.ToString() << " ID=" << debug; qDebug() << "Core::run matrix index " << task.Pos.ToString() << " as tile with " << Matrix.TileAt(task.Pos)->Overlays.count() << " ID=" << debug; #endif // DEBUG_CORE } else { // emit OnTilesStillToLoad(tilesToload); delete t; t = 0; emit OnNeedInvalidation(); } // layers = null; } } { // last buddy cleans stuff ;} if (last) { OPMaps::Instance()->kiberCacheLock.lockForWrite(); OPMaps::Instance()->TilesInMemory.RemoveMemoryOverload(); OPMaps::Instance()->kiberCacheLock.unlock(); MtileDrawingList.lock(); { Matrix.ClearPointsNotIn(tileDrawingList); } MtileDrawingList.unlock(); emit OnTileLoadComplete(); emit OnNeedInvalidation(); } } } #ifdef DEBUG_CORE qDebug() << "loaderLimit release:" + loaderLimit.available() << " ID=" << debug; #endif emit OnTilesStillToLoad(tilesToload < 0 ? 0 : tilesToload); loaderLimit.release(); } } MrunningThreads.lock(); --runningThreads; MrunningThreads.unlock(); } diagnostics Core::GetDiagnostics() { MrunningThreads.lock(); diag = OPMaps::Instance()->GetDiagnostics(); diag.runningThreads = runningThreads; MrunningThreads.unlock(); return diag; } void Core::SetZoom(const int &value) { if (!isDragging) { zoom = value; minOfTiles = Projection()->GetTileMatrixMinXY(value); maxOfTiles = Projection()->GetTileMatrixMaxXY(value); currentPositionPixel = Projection()->FromLatLngToPixel(currentPosition, value); if (started) { MtileLoadQueue.lock(); tileLoadQueue.clear(); MtileLoadQueue.unlock(); MtileToload.lock(); tilesToload = 0; MtileToload.unlock(); Matrix.Clear(); GoToCurrentPositionOnZoom(); UpdateBounds(); keepInBounds(); emit OnMapDrag(); emit OnMapZoomChanged(); emit OnNeedInvalidation(); } } } void Core::SetCurrentPosition(const PointLatLng &value) { if (!IsDragging()) { currentPosition = value; SetCurrentPositionGPixel(Projection()->FromLatLngToPixel(value, Zoom())); if (started) { GoToCurrentPosition(); emit OnCurrentPositionChanged(currentPosition); } } else { currentPosition = value; SetCurrentPositionGPixel(Projection()->FromLatLngToPixel(value, Zoom())); if (started) { emit OnCurrentPositionChanged(currentPosition); } } } void Core::SetMapType(const MapType::Types &value) { if (value != GetMapType()) { mapType = value; switch (value) { case MapType::ArcGIS_Map: case MapType::ArcGIS_Satellite: case MapType::ArcGIS_ShadedRelief: case MapType::ArcGIS_Terrain: { if (Projection()->Type() != "PlateCarreeProjection") { SetProjection(new PlateCarreeProjection()); maxzoom = 13; } } break; case MapType::ArcGIS_MapsLT_Map_Hybrid: case MapType::ArcGIS_MapsLT_Map_Labels: case MapType::ArcGIS_MapsLT_Map: case MapType::ArcGIS_MapsLT_OrtoFoto: { if (Projection()->Type() != "LKS94Projection") { SetProjection(new LKS94Projection()); maxzoom = 11; } } break; case MapType::PergoTurkeyMap: { if (Projection()->Type() != "PlateCarreeProjectionPergo") { SetProjection(new PlateCarreeProjectionPergo()); maxzoom = 17; } } break; case MapType::YandexMapRu: { if (Projection()->Type() != "MercatorProjectionYandex") { SetProjection(new MercatorProjectionYandex()); maxzoom = 13; } } break; default: { if (Projection()->Type() != "MercatorProjection") { SetProjection(new MercatorProjection()); maxzoom = 21; } } break; } minOfTiles = Projection()->GetTileMatrixMinXY(Zoom()); maxOfTiles = Projection()->GetTileMatrixMaxXY(Zoom()); SetCurrentPositionGPixel(Projection()->FromLatLngToPixel(CurrentPosition(), Zoom())); if (started) { CancelAsyncTasks(); OnMapSizeChanged(Width, Height); GoToCurrentPosition(); ReloadMap(); GoToCurrentPosition(); emit OnMapTypeChanged(value); } } } void Core::StartSystem() { if (!started) { started = true; ReloadMap(); GoToCurrentPosition(); } } void Core::UpdateCenterTileXYLocation() { PointLatLng center = FromLocalToLatLng(Width / 2, Height / 2); Point centerPixel = Projection()->FromLatLngToPixel(center, Zoom()); centerTileXYLocation = Projection()->FromPixelToTileXY(centerPixel); } void Core::OnMapSizeChanged(int const & width, int const & height) { Width = width; Height = height; sizeOfMapArea.SetWidth(1 + (Width / Projection()->TileSize().Width()) / 2); sizeOfMapArea.SetHeight(1 + (Height / Projection()->TileSize().Height()) / 2); UpdateCenterTileXYLocation(); if (started) { UpdateBounds(); emit OnCurrentPositionChanged(currentPosition); } } void Core::OnMapClose() { // if(waitOnEmptyTasks != null) // { // try // { // waitOnEmptyTasks.Set(); // waitOnEmptyTasks.Close(); // } // catch // { // } // } CancelAsyncTasks(); } GeoCoderStatusCode::Types Core::SetCurrentPositionByKeywords(QString const & keys) { GeoCoderStatusCode::Types status = GeoCoderStatusCode::Unknow; PointLatLng pos = OPMaps::Instance()->GetLatLngFromGeodecoder(keys, status); if (!pos.IsEmpty() && (status == GeoCoderStatusCode::G_GEO_SUCCESS)) { SetCurrentPosition(pos); } return status; } RectLatLng Core::CurrentViewArea() { PointLatLng p = Projection()->FromPixelToLatLng(-renderOffset.X(), -renderOffset.Y(), Zoom()); double rlng = Projection()->FromPixelToLatLng(-renderOffset.X() + Width, -renderOffset.Y(), Zoom()).Lng(); double blat = Projection()->FromPixelToLatLng(-renderOffset.X(), -renderOffset.Y() + Height, Zoom()).Lat(); return RectLatLng::FromLTRB(p.Lng(), p.Lat(), rlng, blat); } PointLatLng Core::FromLocalToLatLng(int const & x, int const & y) { return Projection()->FromPixelToLatLng(Point(x - renderOffset.X(), y - renderOffset.Y()), Zoom()); } Point Core::FromLatLngToLocal(PointLatLng const & latlng) { Point pLocal = Projection()->FromLatLngToPixel(latlng, Zoom()); pLocal.Offset(renderOffset); return pLocal; } int Core::GetMaxZoomToFitRect(RectLatLng const & rect) { int zoom = 0; for (int i = 1; i <= MaxZoom(); i++) { Point p1 = Projection()->FromLatLngToPixel(rect.LocationTopLeft(), i); Point p2 = Projection()->FromLatLngToPixel(rect.Bottom(), rect.Right(), i); if (((p2.X() - p1.X()) <= Width + 10) && (p2.Y() - p1.Y()) <= Height + 10) { zoom = i; } else { break; } } return zoom; } void Core::BeginDrag(Point const & pt) { dragPoint.SetX(pt.X() - renderOffset.X()); dragPoint.SetY(pt.Y() - renderOffset.Y()); isDragging = true; } void Core::EndDrag() { isDragging = false; emit OnNeedInvalidation(); } void Core::ReloadMap() { if (started) { #ifdef DEBUG_CORE qDebug() << "------------------"; #endif // DEBUG_CORE MtileLoadQueue.lock(); { tileLoadQueue.clear(); } MtileLoadQueue.unlock(); MtileToload.lock(); tilesToload = 0; MtileToload.unlock(); Matrix.Clear(); emit OnNeedInvalidation(); } } void Core::GoToCurrentPosition() { // reset stuff renderOffset = Point::Empty; centerTileXYLocationLast = Point::Empty; dragPoint = Point::Empty; // goto location Drag(Point(-(GetcurrentPositionGPixel().X() - Width / 2), -(GetcurrentPositionGPixel().Y() - Height / 2))); } void Core::GoToCurrentPositionOnZoom() { // reset stuff renderOffset = Point::Empty; centerTileXYLocationLast = Point::Empty; dragPoint = Point::Empty; // goto location and centering if (MouseWheelZooming) { if (mousewheelzoomtype != MouseWheelZoomType::MousePositionWithoutCenter) { Point pt = Point(-(GetcurrentPositionGPixel().X() - Width / 2), -(GetcurrentPositionGPixel().Y() - Height / 2)); renderOffset.SetX(pt.X() - dragPoint.X()); renderOffset.SetY(pt.Y() - dragPoint.Y()); } else { // without centering renderOffset.SetX(-GetcurrentPositionGPixel().X() - dragPoint.X()); renderOffset.SetY(-GetcurrentPositionGPixel().Y() - dragPoint.Y()); renderOffset.Offset(mouseLastZoom); } } else { // use current map center mouseLastZoom = Point::Empty; Point pt = Point(-(GetcurrentPositionGPixel().X() - Width / 2), -(GetcurrentPositionGPixel().Y() - Height / 2)); renderOffset.SetX(pt.X() - dragPoint.X()); renderOffset.SetY(pt.Y() - dragPoint.Y()); } UpdateCenterTileXYLocation(); } void Core::DragOffset(Point const & offset) { renderOffset.Offset(offset); UpdateCenterTileXYLocation(); if (centerTileXYLocation != centerTileXYLocationLast) { centerTileXYLocationLast = centerTileXYLocation; UpdateBounds(); } { LastLocationInBounds = CurrentPosition(); SetCurrentPosition(FromLocalToLatLng((int)Width / 2, (int)Height / 2)); } emit OnNeedInvalidation(); emit OnMapDrag(); } void Core::Drag(Point const & pt) { renderOffset.SetX(pt.X() - dragPoint.X()); renderOffset.SetY(pt.Y() - dragPoint.Y()); keepInBounds(); UpdateCenterTileXYLocation(); if (centerTileXYLocation != centerTileXYLocationLast) { centerTileXYLocationLast = centerTileXYLocation; UpdateBounds(); } if (IsDragging()) { LastLocationInBounds = CurrentPosition(); SetCurrentPosition(FromLocalToLatLng((int)Width / 2, (int)Height / 2)); } emit OnNeedInvalidation(); emit OnMapDrag(); } void Core::CancelAsyncTasks() { if (started) { ProcessLoadTaskCallback.waitForDone(); MtileLoadQueue.lock(); { tileLoadQueue.clear(); // tilesToload=0; } MtileLoadQueue.unlock(); MtileToload.lock(); tilesToload = 0; MtileToload.unlock(); // ProcessLoadTaskCallback.waitForDone(); } } void Core::UpdateBounds() { MtileDrawingList.lock(); { FindTilesAround(tileDrawingList); #ifdef DEBUG_CORE qDebug() << "OnTileLoadStart: " << tileDrawingList.count() << " tiles to load at zoom " << Zoom() << ", time: " << QDateTime::currentDateTime().date(); #endif // DEBUG_CORE emit OnTileLoadStart(); foreach(Point p, tileDrawingList) { LoadTask task = LoadTask(p, Zoom()); { MtileLoadQueue.lock(); { if (!tileLoadQueue.contains(task)) { MtileToload.lock(); ++tilesToload; MtileToload.unlock(); tileLoadQueue.enqueue(task); #ifdef DEBUG_CORE qDebug() << "Core::UpdateBounds new Task" << task.Pos.ToString(); #endif // DEBUG_CORE ProcessLoadTaskCallback.start(this); } } MtileLoadQueue.unlock(); } } } MtileDrawingList.unlock(); UpdateGroundResolution(); } void Core::FindTilesAround(QList &list) { list.clear();; for (int i = -sizeOfMapArea.Width(); i <= sizeOfMapArea.Width(); i++) { for (int j = -sizeOfMapArea.Height(); j <= sizeOfMapArea.Height(); j++) { Point p = centerTileXYLocation; p.SetX(p.X() + i); p.SetY(p.Y() + j); // if(p.X < minOfTiles.Width) // { // p.X += (maxOfTiles.Width + 1); // } // if(p.X > maxOfTiles.Width) // { // p.X -= (maxOfTiles.Width + 1); // } if (p.X() >= minOfTiles.Width() && p.Y() >= minOfTiles.Height() && p.X() <= maxOfTiles.Width() && p.Y() <= maxOfTiles.Height()) { if (!list.contains(p)) { list.append(p); } } } } } void Core::UpdateGroundResolution() { double rez = Projection()->GetGroundResolution(Zoom(), CurrentPosition().Lat()); pxRes100m = (int)(100.0 / rez); // 100 meters pxRes1000m = (int)(1000.0 / rez); // 1km pxRes10km = (int)(10000.0 / rez); // 10km pxRes100km = (int)(100000.0 / rez); // 100km pxRes1000km = (int)(1000000.0 / rez); // 1000km pxRes5000km = (int)(5000000.0 / rez); // 5000km } void Core::keepInBounds() { if (renderOffset.X() > 0) { renderOffset.SetX(0); } if (renderOffset.Y() > 0) { renderOffset.SetY(0); } int maxDragY = GetCurrentRegion().Height() - GettileRect().Height() * (maxOfTiles.Height() - minOfTiles.Height() + 1); int maxDragX = GetCurrentRegion().Width() - GettileRect().Width() * (maxOfTiles.Width() - minOfTiles.Width() + 1); if (maxDragY > renderOffset.Y()) { renderOffset.SetY(maxDragY); } if (maxDragX > renderOffset.X()) { renderOffset.SetX(maxDragX); } } }