summaryrefslogtreecommitdiff
path: root/src/mbgl/util/geo.cpp
blob: 1106779474b1845f13ac7749a3dfae580c926338 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <mbgl/util/geo.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_range.hpp>

#include <cmath>

namespace mbgl {

namespace {

double lat_(const uint8_t z, const int64_t y) {
    const double n = M_PI - 2.0 * M_PI * y / std::pow(2.0, z);
    return util::RAD2DEG * std::atan(0.5 * (std::exp(n) - std::exp(-n)));
}

double lon_(const uint8_t z, const int64_t x) {
    return x / std::pow(2.0, z) * util::DEGREES_MAX - util::LONGITUDE_MAX;
}

} // end namespace

LatLng::LatLng(const CanonicalTileID& id) : lat(lat_(id.z, id.y)), lon(lon_(id.z, id.x)) {
}

LatLng::LatLng(const UnwrappedTileID& id)
    : lat(lat_(id.canonical.z, id.canonical.y)),
      lon(lon_(id.canonical.z, id.canonical.x) + id.wrap * util::DEGREES_MAX) {
}

LatLngBounds::LatLngBounds(const CanonicalTileID& id)
    : sw({ lat_(id.z, id.y + 1), lon_(id.z, id.x) }),
      ne({ lat_(id.z, id.y), lon_(id.z, id.x + 1) }) {
}

bool LatLngBounds::contains(const CanonicalTileID& tileID) const {
    return util::TileRange::fromLatLngBounds(*this, tileID.z).contains(tileID);
}

bool LatLngBounds::contains(const LatLng& point, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const {
    return containsLatitude(point.latitude()) && containsLongitude(point.longitude(), wrap);
}

bool LatLngBounds::contains(const LatLngBounds& area, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const {
    bool containsAreaLatitude = area.north() <= north() && area.south() >= south();
    if (!containsAreaLatitude) {
        return false;
    }

    bool containsUnwrapped = area.east() <= east() && area.west() >= west();
    if(containsUnwrapped) {
        return true;
    } else if (wrap == LatLng::Wrapped) {
        LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
        LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
        if (crossesAntimeridian() & !area.crossesAntimeridian()) {
            return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) ||
                   (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX);
        } else {
            return other.east() <= wrapped.east() && other.west() >= wrapped.west();
        }
    }
    return false;
}

bool LatLngBounds::intersects(const LatLngBounds area, LatLng::WrapMode wrap /*= LatLng::Unwrapped*/) const {
    bool latitudeIntersects = area.north() > south() && area.south() < north();
    if (!latitudeIntersects) {
        return false;
    }

    bool longitudeIntersects = area.east() > west() && area.west() < east();
    if (longitudeIntersects) {
        return true;
    } else if (wrap == LatLng::Wrapped) {
        LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
        LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
        if (crossesAntimeridian()) {
            return area.crossesAntimeridian() ||
                   other.east() > wrapped.west() ||
                   other.west() < wrapped.east();
        } else if (other.crossesAntimeridian()){
            return other.east() > wrapped.west() ||
                   other.west() < wrapped.east();
        } else {
            return other.east() > wrapped.west() &&
                   other.west() < wrapped.east();
        }
    }
    return false;
}

LatLng LatLngBounds::constrain(const LatLng& p) const {
    if (!bounded) {
        return p;
    }

    double lat = p.latitude();
    double lng = p.longitude();

    if (!containsLatitude(lat)) {
        lat = util::clamp(lat, south(), north());
    }

    if (!containsLongitude(lng, LatLng::Unwrapped)) {
        lng = util::clamp(lng, west(), east());
    }

    return LatLng { lat, lng };
}

bool LatLngBounds::containsLatitude(double latitude) const {
    return latitude >= south() && latitude <= north();
}

bool LatLngBounds::containsLongitude(double longitude, LatLng::WrapMode wrap) const {
    if (longitude >= west() && longitude <= east()) {
        return true;
    }

    if (wrap == LatLng::Wrapped) {
        LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
        longitude = LatLng(0.0, longitude).wrapped().longitude();
        if (crossesAntimeridian()) {
            return (longitude >= -util::LONGITUDE_MAX && longitude <= wrapped.east()) ||
                   (longitude >= wrapped.west() && longitude <= util::LONGITUDE_MAX);
        }

        return longitude >= wrapped.west() && longitude <= wrapped.east();
    }

    return false;
}

ScreenCoordinate EdgeInsets::getCenter(uint16_t width, uint16_t height) const {
    return {
        (width - left() - right()) / 2.0 + left(),
        (height - top() - bottom()) / 2.0 + top(),
    };
}

} // end namespace mbgl