Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions include/mbgl/util/projection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@

#include <mbgl/util/constants.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/math/clamp.hpp>

#include <cmath>

namespace mbgl {

// Spherical Mercator projection
// http://docs.openlayers.org/library/spherical_mercator.html
class Projection {

public:
// Map pixel width at given scale.
static double worldSize(double scale) {
return scale * util::tileSize;
}

static double getMetersPerPixelAtLatitude(double lat, double zoom) {
const double constrainedZoom = util::clamp(zoom, util::MIN_ZOOM, util::MAX_ZOOM);
const double mapPixelWidthAtZoom = std::pow(2.0, constrainedZoom) * util::tileSize;
const double constrainedScale = std::pow(2.0, constrainedZoom);
const double constrainedLatitude = util::clamp(lat, -util::LATITUDE_MAX, util::LATITUDE_MAX);
return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / mapPixelWidthAtZoom;
return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / worldSize(constrainedScale);
}

static ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) {
Expand All @@ -32,14 +38,30 @@ class Projection {
}

static LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) {
double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2)) * util::RAD2DEG;
double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG;
double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M;

latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX);
longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);

return LatLng(latitude, longitude);
}

static Point<double> project(const LatLng& latLng, double scale) {
return Point<double> {
util::LONGITUDE_MAX + latLng.longitude,
util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude * M_PI / util::DEGREES_MAX))
} * worldSize(scale) / util::DEGREES_MAX;
}

static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
auto p2 = p * util::DEGREES_MAX / worldSize(scale);
return LatLng {
util::DEGREES_MAX / M_PI * std::atan(std::exp((util::LONGITUDE_MAX - p2.y) * util::DEG2RAD)) - 90.0,
p2.x - util::LONGITUDE_MAX,
wrapMode
};
}
};

} // namespace mbgl
25 changes: 5 additions & 20 deletions src/mbgl/annotation/symbol_annotation_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_coordinate.hpp>

namespace mbgl {

Expand All @@ -14,26 +15,10 @@ void SymbolAnnotationImpl::updateLayer(const CanonicalTileID& tileID, Annotation
std::unordered_map<std::string, std::string> featureProperties;
featureProperties.emplace("sprite", annotation.icon.empty() ? std::string("default_marker") : annotation.icon);

const Point<double>& p = annotation.geometry;

// Clamp to the latitude limits of Web Mercator.
const double constrainedLatitude = util::clamp(p.y, -util::LATITUDE_MAX, util::LATITUDE_MAX);

// Project a coordinate into unit space in a square map.
const double sine = std::sin(constrainedLatitude * util::DEG2RAD);
const double x = p.x / util::DEGREES_MAX + 0.5;
const double y = 0.5 - 0.25 * std::log((1.0 + sine) / (1.0 - sine)) / M_PI;

Point<double> projected(x, y);
projected *= std::pow(2, tileID.z);
projected.x = std::fmod(projected.x, 1);
projected.y = std::fmod(projected.y, 1);
projected *= double(util::EXTENT);

layer.features.emplace_back(id,
FeatureType::Point,
GeometryCollection {{ {{ convertPoint<int16_t>(projected) }} }},
featureProperties);
LatLng latLng { annotation.geometry.y, annotation.geometry.x };
TileCoordinate coordinate = TileCoordinate::fromLatLng(0, latLng);
GeometryCoordinate tilePoint = TileCoordinate::toGeometryCoordinate(UnwrappedTileID(0, tileID), coordinate.p);
layer.features.emplace_back(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties);
}

} // namespace mbgl
22 changes: 8 additions & 14 deletions src/mbgl/map/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <mbgl/util/unitbezier.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
Expand Down Expand Up @@ -112,8 +113,8 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
// Find the shortest path otherwise.
else startLatLng.unwrapForShortestPath(latLng);

const Point<double> startPoint = state.project(startLatLng);
const Point<double> endPoint = state.project(latLng);
const Point<double> startPoint = Projection::project(startLatLng, state.scale);
const Point<double> endPoint = Projection::project(latLng, state.scale);

ScreenCoordinate center = getScreenCoordinate(padding);
center.y = state.height - center.y;
Expand All @@ -131,10 +132,6 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim

Duration duration = animation.duration ? *animation.duration : Duration::zero();

const double startWorldSize = state.worldSize();
state.Bc = startWorldSize / util::DEGREES_MAX;
state.Cc = startWorldSize / util::M2PI;

const double startScale = state.scale;
const double startAngle = state.angle;
const double startPitch = state.pitch;
Expand All @@ -144,7 +141,7 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim

startTransition(camera, animation, [=](double t) {
Point<double> framePoint = util::interpolate(startPoint, endPoint, t);
LatLng frameLatLng = state.unproject(framePoint, startWorldSize);
LatLng frameLatLng = Projection::unproject(framePoint, startScale);
double frameScale = util::interpolate(startScale, scale, t);
state.setLatLngZoom(frameLatLng, state.scaleZoom(frameScale));

Expand Down Expand Up @@ -186,8 +183,8 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
LatLng startLatLng = getLatLng(padding).wrapped();
startLatLng.unwrapForShortestPath(latLng);

const Point<double> startPoint = state.project(startLatLng);
const Point<double> endPoint = state.project(latLng);
const Point<double> startPoint = Projection::project(startLatLng, state.scale);
const Point<double> endPoint = Projection::project(latLng, state.scale);

ScreenCoordinate center = getScreenCoordinate(padding);
center.y = state.height - center.y;
Expand Down Expand Up @@ -291,10 +288,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
return;
}

const double startWorldSize = state.worldSize();
state.Bc = startWorldSize / util::DEGREES_MAX;
state.Cc = startWorldSize / util::M2PI;

const double startScale = state.scale;
state.panning = true;
state.scaling = true;
state.rotating = angle != startAngle;
Expand All @@ -310,7 +304,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
double frameZoom = startZoom + state.scaleZoom(1 / w(s));

// Convert to geographic coordinates and set the new viewpoint.
LatLng frameLatLng = state.unproject(framePoint, startWorldSize);
LatLng frameLatLng = Projection::unproject(framePoint, startScale);
state.setLatLngZoom(frameLatLng, frameZoom);

if (angle != startAngle) {
Expand Down
44 changes: 12 additions & 32 deletions src/mbgl/map/transform_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ TransformState::TransformState(ConstrainMode constrainMode_, ViewportMode viewpo

void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) const {
const uint64_t tileScale = 1ull << tileID.canonical.z;
const double s = worldSize() / tileScale;
const double s = Projection::worldSize(scale) / tileScale;

matrix::identity(matrix);
matrix::translate(matrix, matrix,
Expand Down Expand Up @@ -108,12 +108,12 @@ LatLng TransformState::getLatLng(LatLng::WrapMode wrapMode) const {
}

double TransformState::pixel_x() const {
const double center = (width - worldSize()) / 2;
const double center = (width - Projection::worldSize(scale)) / 2;
return center + x;
}

double TransformState::pixel_y() const {
const double center = (height - worldSize()) / 2;
const double center = (height - Projection::worldSize(scale)) / 2;
return center + y;
}

Expand Down Expand Up @@ -201,22 +201,6 @@ bool TransformState::isGestureInProgress() const {

#pragma mark - Projection

Point<double> TransformState::project(const LatLng& ll) const {
return Point<double>(
(util::LONGITUDE_MAX + ll.longitude),
(util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + ll.latitude * M_PI / util::DEGREES_MAX)))
) * worldSize() / util::DEGREES_MAX;
}

LatLng TransformState::unproject(const Point<double>& p, double worldSize, LatLng::WrapMode wrapMode) const {
Point<double> p2 = p * util::DEGREES_MAX / worldSize;
return LatLng(
util::DEGREES_MAX / M_PI * std::atan(std::exp((util::LONGITUDE_MAX - p2.y) * util::DEG2RAD)) - 90.0f,
p2.x - util::LONGITUDE_MAX,
wrapMode
);
}

double TransformState::zoomScale(double zoom) const {
return std::pow(2.0f, zoom);
}
Expand All @@ -225,18 +209,14 @@ double TransformState::scaleZoom(double s) const {
return util::log2(s);
}

double TransformState::worldSize() const {
return scale * util::tileSize;
}

ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) const {
if (width == 0 || height == 0) {
return {};
}

mat4 mat = coordinatePointMatrix(getZoom());
vec4 p;
Point<double> pt = project(latLng) / double(util::tileSize);
Point<double> pt = Projection::project(latLng, scale) / double(util::tileSize);
vec4 c = {{ pt.x, pt.y, 0, 1 }};
matrix::transformMat4(p, c, mat);
return { p[0] / p[3], height - p[1] / p[3] };
Expand Down Expand Up @@ -278,13 +258,13 @@ LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, L
double z1 = coord1[2] / w1;
double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0);

return unproject(util::interpolate(p0, p1, t), scale, wrapMode);
return Projection::unproject(util::interpolate(p0, p1, t), scale / util::tileSize, wrapMode);
}

mat4 TransformState::coordinatePointMatrix(double z) const {
mat4 proj;
getProjMatrix(proj);
float s = worldSize() / std::pow(2, z);
float s = Projection::worldSize(scale) / std::pow(2, z);
matrix::scale(proj, proj, s, s, 1);
matrix::multiply(proj, getPixelMatrix(), proj);
return proj;
Expand Down Expand Up @@ -325,10 +305,10 @@ void TransformState::constrain(double& scale_, double& x_, double& y_) const {
}

void TransformState::moveLatLng(const LatLng& latLng, const ScreenCoordinate& anchor) {
auto centerCoord = project(getLatLng(LatLng::Unwrapped));
auto latLngCoord = project(latLng);
auto anchorCoord = project(screenCoordinateToLatLng(anchor));
setLatLngZoom(unproject(centerCoord + latLngCoord - anchorCoord, worldSize()), getZoom());
auto centerCoord = Projection::project(getLatLng(LatLng::Unwrapped), scale);
auto latLngCoord = Projection::project(latLng, scale);
auto anchorCoord = Projection::project(screenCoordinateToLatLng(anchor), scale);
setLatLngZoom(Projection::unproject(centerCoord + latLngCoord - anchorCoord, scale), getZoom());
}

void TransformState::setLatLngZoom(const LatLng &latLng, double zoom) {
Expand All @@ -355,8 +335,8 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate
scale = constrainedScale;
x = constrainedPoint.x;
y = constrainedPoint.y;
Bc = worldSize() / util::DEGREES_MAX;
Cc = worldSize() / util::M2PI;
Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
Cc = Projection::worldSize(scale) / util::M2PI;
}

} // namespace mbgl
12 changes: 4 additions & 8 deletions src/mbgl/map/transform_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/mat4.hpp>

#include <cstdint>
Expand Down Expand Up @@ -65,13 +66,10 @@ class TransformState {
bool isPanning() const;
bool isGestureInProgress() const;

// Conversion and projection
// Conversion
ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const;
LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Unwrapped) const;

Point<double> project(const LatLng&) const;
LatLng unproject(const Point<double>&, double worldSize, LatLng::WrapMode = LatLng::Unwrapped) const;

double zoomScale(double zoom) const;
double scaleZoom(double scale) const;

Expand All @@ -88,8 +86,6 @@ class TransformState {
// logical dimensions
uint16_t width = 0, height = 0;

double worldSize() const;

mat4 coordinatePointMatrix(double z) const;
mat4 getPixelMatrix() const;

Expand Down Expand Up @@ -117,8 +113,8 @@ class TransformState {
double pitch = 0.0;

// cache values for spherical mercator math
double Bc = worldSize() / util::DEGREES_MAX;
double Cc = worldSize() / util::M2PI;
double Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
double Cc = Projection::worldSize(scale) / util::M2PI;
};

} // namespace mbgl
15 changes: 9 additions & 6 deletions src/mbgl/util/tile_coordinate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/projection.hpp>

namespace mbgl {

Expand All @@ -17,23 +18,25 @@ class TileCoordinate {
TileCoordinatePoint p;
double z;

static TileCoordinate fromLatLng(const TransformState& state, double zoom, const LatLng& latLng) {
const double scale = std::pow(2, zoom - state.getZoom());
return { state.project(latLng) * scale / double(util::tileSize), zoom };
static TileCoordinate fromLatLng(double zoom, const LatLng& latLng) {
const double scale = std::pow(2.0, zoom);
return { Projection::project(latLng, scale) / double(util::tileSize), zoom };
}

static TileCoordinate fromScreenCoordinate(const TransformState& state, double zoom, const ScreenCoordinate& screenCoordinate) {
return fromLatLng(state, zoom, state.screenCoordinateToLatLng(screenCoordinate));
return fromLatLng(zoom, state.screenCoordinateToLatLng(screenCoordinate));
}

TileCoordinate zoomTo(double zoom) const {
return { p * std::pow(2, zoom - z), zoom };
const double scaleDiff = std::pow(2.0, zoom - z);
return { p * scaleDiff, zoom };
}

static GeometryCoordinate toGeometryCoordinate(const UnwrappedTileID& tileID, const TileCoordinatePoint& point) {
const double scale = std::pow(2.0, tileID.canonical.z);
auto zoomed = TileCoordinate { point, 0 }.zoomTo(tileID.canonical.z);
return {
int16_t(util::clamp<int64_t>((zoomed.p.x - tileID.canonical.x - tileID.wrap * std::pow(2.0, tileID.canonical.z)) * util::EXTENT,
int16_t(util::clamp<int64_t>((zoomed.p.x - tileID.canonical.x - tileID.wrap * scale) * util::EXTENT,
std::numeric_limits<int16_t>::min(),
std::numeric_limits<int16_t>::max())),
int16_t(util::clamp<int64_t>((zoomed.p.y - tileID.canonical.y) * util::EXTENT,
Expand Down
11 changes: 5 additions & 6 deletions src/mbgl/util/tile_cover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,12 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
{ std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() },
{ std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });

const TransformState state;
return tileCover(
TileCoordinate::fromLatLng(state, z, bounds.northwest()).p,
TileCoordinate::fromLatLng(state, z, bounds.northeast()).p,
TileCoordinate::fromLatLng(state, z, bounds.southeast()).p,
TileCoordinate::fromLatLng(state, z, bounds.southwest()).p,
TileCoordinate::fromLatLng(state, z, bounds.center()).p,
TileCoordinate::fromLatLng(z, bounds.northwest()).p,
TileCoordinate::fromLatLng(z, bounds.northeast()).p,
TileCoordinate::fromLatLng(z, bounds.southeast()).p,
TileCoordinate::fromLatLng(z, bounds.southwest()).p,
TileCoordinate::fromLatLng(z, bounds.center()).p,
z);
}

Expand Down
Loading