void QDeclarativeGeoMap::fitViewportToGeoShape() { double bboxWidth; double bboxHeight; QGeoCoordinate centerCoordinate; switch (m_region.type()) { case QGeoShape::RectangleType: { QGeoRectangle rect = m_region; QDoubleVector2D topLeftPoint = m_map->coordinateToItemPosition(rect.topLeft(), false); QDoubleVector2D botRightPoint = m_map->coordinateToItemPosition(rect.bottomRight(), false); bboxWidth = qAbs(topLeftPoint.x() - botRightPoint.x()); bboxHeight = qAbs(topLeftPoint.y() - botRightPoint.y()); centerCoordinate = rect.center(); break; } case QGeoShape::CircleType: { QGeoCircle circle = m_region; centerCoordinate = circle.center(); QGeoCoordinate edge = centerCoordinate.atDistanceAndAzimuth(circle.radius(), 90); QDoubleVector2D centerPoint = m_map->coordinateToItemPosition(centerCoordinate, false); QDoubleVector2D edgePoint = m_map->coordinateToItemPosition(edge, false); bboxWidth = qAbs(centerPoint.x() - edgePoint.x()) * 2; bboxHeight = bboxWidth; break; } case QGeoShape::UnknownType: //Fallthrough to default default: return; } // position camera to the center of bounding box setProperty("center", QVariant::fromValue(centerCoordinate)); //If the shape is empty we just change centerposition, not zoom if (bboxHeight == 0 && bboxWidth == 0) return; // adjust zoom double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); double mapWidthRatio = width() / (width() + height()); double zoomRatio; if (bboxWidthRatio > mapWidthRatio) zoomRatio = bboxWidth / width(); else zoomRatio = bboxHeight / height(); qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); newZoom = std::floor(qMax(minimumZoomLevel(), (m_map->mapController()->zoom() + newZoom))); setProperty("zoomLevel", QVariant::fromValue(newZoom)); }
/*! \internal Sets the gesture areas maximum zoom level. If the camera capabilities has been set this method honors the boundaries set by it. */ void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel) { if (gestureArea_ && maximumZoomLevel >= 0) { if (mappingManagerInitialized_ && maximumZoomLevel > mappingManager_->cameraCapabilities().maximumZoomLevel()) { maximumZoomLevel = mappingManager_->cameraCapabilities().maximumZoomLevel(); } gestureArea_->setMaximumZoomLevel(maximumZoomLevel); setZoomLevel(qBound<qreal>(minimumZoomLevel(), zoomLevel(), maximumZoomLevel)); } }
/*! \internal Sets the gesture areas maximum zoom level. If the camera capabilities has been set this method honors the boundaries set by it. */ void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel) { if (m_gestureArea && maximumZoomLevel >= 0) { qreal oldMaximumZoomLevel = this->maximumZoomLevel(); if (m_mappingManagerInitialized && maximumZoomLevel > m_mappingManager->cameraCapabilities().maximumZoomLevel()) { maximumZoomLevel = m_mappingManager->cameraCapabilities().maximumZoomLevel(); } m_gestureArea->setMaximumZoomLevel(maximumZoomLevel); setZoomLevel(qBound<qreal>(minimumZoomLevel(), zoomLevel(), maximumZoomLevel)); if (oldMaximumZoomLevel != maximumZoomLevel) emit maximumZoomLevelChanged(); } }
/*! \qmlproperty real QtLocation::Map::zoomLevel This property holds the zoom level for the map. Larger values for the zoom level provide more detail. Zoom levels are always non-negative. The default value is 8.0. */ void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) { if (zoomLevel_ == zoomLevel || zoomLevel < 0) return; if ((zoomLevel < minimumZoomLevel() || (maximumZoomLevel() >= 0 && zoomLevel > maximumZoomLevel()))) return; zoomLevel_ = zoomLevel; if (mappingManagerInitialized_) map_->mapController()->setZoom(zoomLevel_); emit zoomLevelChanged(zoomLevel); }
/*! \qmlproperty real QtLocation::Map::zoomLevel This property holds the zoom level for the map. Larger values for the zoom level provide more detail. Zoom levels are always non-negative. The default value is 8.0. */ void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) { if (m_zoomLevel == zoomLevel || zoomLevel < 0) return; if ((zoomLevel < minimumZoomLevel() || (maximumZoomLevel() >= 0 && zoomLevel > maximumZoomLevel()))) return; m_zoomLevel = zoomLevel; m_validRegion = false; if (m_mappingManagerInitialized) m_map->mapController()->setZoom(m_zoomLevel); emit zoomLevelChanged(zoomLevel); }
void QDeclarativeGeoMap::fitViewportToGeoShape() { int margins = 10; if (!m_map || width() <= margins || height() <= margins) return; QGeoCoordinate topLeft; QGeoCoordinate bottomRight; switch (m_region.type()) { case QGeoShape::RectangleType: { QGeoRectangle rect = m_region; topLeft = rect.topLeft(); bottomRight = rect.bottomRight(); break; } case QGeoShape::CircleType: { const double pi = M_PI; QGeoCircle circle = m_region; QGeoCoordinate centerCoordinate = circle.center(); // calculate geo bounding box of the circle // circle tangential points with meridians and the north pole create // spherical triangle, we use spherical law of sines // sin(lon_delta_in_rad)/sin(r_in_rad) = // sin(alpha_in_rad)/sin(pi/2 - lat_in_rad), where: // * lon_delta_in_rad - delta of longitudes of circle center // and tangential points // * r_in_rad - angular radius of the circle // * lat_in_rad - latitude of circle center // * alpha_in_rad - angle between meridian and radius to the circle => // this is tangential point => sin(alpha) = 1 // * lat_delta_in_rad - delta of latitudes of circle center and // latitude of points where great circle (going through circle // center) crosses circle and the pole double r_in_rad = circle.radius() / EARTH_MEAN_RADIUS; // angular r double lat_delta_in_deg = r_in_rad * 180 / pi; double lon_delta_in_deg = std::asin(std::sin(r_in_rad) / std::cos(centerCoordinate.latitude() * pi / 180)) * 180 / pi; topLeft.setLatitude(centerCoordinate.latitude() + lat_delta_in_deg); topLeft.setLongitude(centerCoordinate.longitude() - lon_delta_in_deg); bottomRight.setLatitude(centerCoordinate.latitude() - lat_delta_in_deg); bottomRight.setLongitude(centerCoordinate.longitude() + lon_delta_in_deg); // adjust if circle reaches poles => cross all meridians and // fit into Mercator projection bounds if (topLeft.latitude() > 90 || bottomRight.latitude() < -90) { topLeft.setLatitude(qMin(topLeft.latitude(), 85.05113)); topLeft.setLongitude(-180.0); bottomRight.setLatitude(qMax(bottomRight.latitude(), -85.05113)); bottomRight.setLongitude(180.0); } break; } case QGeoShape::UnknownType: //Fallthrough to default default: return; } // adjust zoom, use reference world to keep things simple // otherwise we would need to do the error prone longitudes // wrapping QDoubleVector2D topLeftPoint = m_map->referenceCoordinateToItemPosition(topLeft); QDoubleVector2D bottomRightPoint = m_map->referenceCoordinateToItemPosition(bottomRight); double bboxWidth = bottomRightPoint.x() - topLeftPoint.x(); double bboxHeight = bottomRightPoint.y() - topLeftPoint.y(); // find center of the bounding box QGeoCoordinate centerCoordinate = m_map->referenceItemPositionToCoordinate( (topLeftPoint + bottomRightPoint)/2); // position camera to the center of bounding box setCenter(centerCoordinate); // if the shape is empty we just change center position, not zoom if (bboxHeight == 0 && bboxWidth == 0) return; double zoomRatio = qMax(bboxWidth / (width() - margins), bboxHeight / (height() - margins)); // fixme: use log2 with c++11 zoomRatio = std::log(zoomRatio) / std::log(2.0); double newZoom = qMax((double)minimumZoomLevel(), m_map->mapController()->zoom() - zoomRatio); setZoomLevel(newZoom); m_validRegion = true; }
/*! \qmlmethod QtLocation::Map::fitViewportToGeoShape(QGeoShape shape) Fits the current viewport to the boundary of the shape. The camera is positioned in the center of the shape, and at the largest integral zoom level possible which allows the whole shape to be visible on screen */ void QDeclarativeGeoMap::fitViewportToGeoShape(const QVariant &variantShape) { if (!map_ || !mappingManagerInitialized_) return; QGeoShape shape; if (variantShape.userType() == qMetaTypeId<QGeoRectangle>()) shape = variantShape.value<QGeoRectangle>(); else if (variantShape.userType() == qMetaTypeId<QGeoCircle>()) shape = variantShape.value<QGeoCircle>(); else if (variantShape.userType() == qMetaTypeId<QGeoShape>()) shape = variantShape.value<QGeoShape>(); if (!shape.isValid()) return; double bboxWidth; double bboxHeight; QGeoCoordinate centerCoordinate; switch (shape.type()) { case QGeoShape::RectangleType: { QGeoRectangle rect = shape; QDoubleVector2D topLeftPoint = map_->coordinateToScreenPosition(rect.topLeft(), false); QDoubleVector2D botRightPoint = map_->coordinateToScreenPosition(rect.bottomRight(), false); bboxWidth = qAbs(topLeftPoint.x() - botRightPoint.x()); bboxHeight = qAbs(topLeftPoint.y() - botRightPoint.y()); centerCoordinate = rect.center(); break; } case QGeoShape::CircleType: { QGeoCircle circle = shape; centerCoordinate = circle.center(); QGeoCoordinate edge = centerCoordinate.atDistanceAndAzimuth(circle.radius(), 90); QDoubleVector2D centerPoint = map_->coordinateToScreenPosition(centerCoordinate, false); QDoubleVector2D edgePoint = map_->coordinateToScreenPosition(edge, false); bboxWidth = qAbs(centerPoint.x() - edgePoint.x()) * 2; bboxHeight = bboxWidth; break; } case QGeoShape::UnknownType: //Fallthrough to default default: return; } // position camera to the center of bounding box setProperty("center", QVariant::fromValue(centerCoordinate)); //If the shape is empty we just change centerposition, not zoom if (bboxHeight == 0 && bboxWidth == 0) return; // adjust zoom double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); double mapWidthRatio = width() / (width() + height()); double zoomRatio; if (bboxWidthRatio > mapWidthRatio) zoomRatio = bboxWidth / width(); else zoomRatio = bboxHeight / height(); qreal newZoom = log10(zoomRatio) / log10(0.5); newZoom = floor(qMax(minimumZoomLevel(), (map_->mapController()->zoom() + newZoom))); setProperty("zoomLevel", QVariant::fromValue(newZoom)); }
/*! \internal */ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine) { if (mapItems_.size() == 0) return; double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; double topLeftX = 0; double topLeftY = 0; double bottomRightX = 0; double bottomRightY = 0; bool haveQuickItem = false; // find bounds of all map items int itemCount = 0; for (int i = 0; i < mapItems_.count(); ++i) { if (!mapItems_.at(i)) continue; QDeclarativeGeoMapItemBase *item = mapItems_.at(i).data(); if (!item) continue; // skip quick items in the first pass and refine the fit later if (refine) { QDeclarativeGeoMapQuickItem *quickItem = qobject_cast<QDeclarativeGeoMapQuickItem*>(item); if (quickItem) { haveQuickItem = true; continue; } } topLeftX = item->position().x(); topLeftY = item->position().y(); bottomRightX = topLeftX + item->width(); bottomRightY = topLeftY + item->height(); if (itemCount == 0) { minX = topLeftX; maxX = bottomRightX; minY = topLeftY; maxY = bottomRightY; } else { minX = qMin(minX, topLeftX); maxX = qMax(maxX, bottomRightX); minY = qMin(minY, topLeftY); maxY = qMax(maxY, bottomRightY); } ++itemCount; } if (itemCount == 0) { if (haveQuickItem) fitViewportToMapItemsRefine(false); return; } double bboxWidth = maxX - minX; double bboxHeight = maxY - minY; double bboxCenterX = minX + (bboxWidth / 2.0); double bboxCenterY = minY + (bboxHeight / 2.0); // position camera to the center of bounding box QGeoCoordinate coordinate; coordinate = map_->screenPositionToCoordinate(QDoubleVector2D(bboxCenterX, bboxCenterY), false); setProperty("center", QVariant::fromValue(coordinate)); // adjust zoom double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); double mapWidthRatio = width() / (width() + height()); double zoomRatio; if (bboxWidthRatio > mapWidthRatio) zoomRatio = bboxWidth / width(); else zoomRatio = bboxHeight / height(); qreal newZoom = log10(zoomRatio) / log10(0.5); newZoom = floor(qMax(minimumZoomLevel(), (map_->mapController()->zoom() + newZoom))); setProperty("zoomLevel", QVariant::fromValue(newZoom)); // as map quick items retain the same screen size after the camera zooms in/out // we refine the viewport again to achieve better results if (refine) fitViewportToMapItemsRefine(false); }