aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/geohash/geohash.h
blob: 7d270612e8877d7cf8344f6034a81771a6899dfc (plain) (blame)
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
#pragma once

/**
 * @file
 * @brief Strong (because it works) and independent (of contrib/libs/geohash) GeoHash implementation
 * GeoHash algo: https://en.wikipedia.org/wiki/Geohash
 * Useful links:
 * 1. http://geohash.org - Main Site
 * 2. https://dou.ua/lenta/articles/geohash - Geohash-based geopoints clusterization
 * 3. http://www.movable-type.co.uk/scripts/geohash.html - bidirectional encoding and visualization
 */
#include <library/cpp/geohash/direction.h>
#include <library/cpp/geohash/direction.h_serialized.h>

#include <library/cpp/geo/geo.h>

#include <util/generic/maybe.h>
#include <util/generic/string.h>
#include <util/system/types.h>

#include <array>

namespace NGeoHash {
    using TBoundingBoxLL = NGeo::TGeoBoundingBox;
    static constexpr auto directionsCount = GetEnumItemsCount<EDirection>();

    template <class T>
    class TNeighbours: public std::array<T, directionsCount> {
    public:
        TNeighbours() = default;

        TNeighbours(std::initializer_list<T> list) {
            Y_ASSERT(list.size() == directionsCount);
            std::copy(list.begin(), list.end(), std::array<T, directionsCount>::begin());
        }

        const T& operator[](EDirection direction) const {
            return std::array<T, directionsCount>::operator[](static_cast<size_t>(direction));
        }

        T& operator[](EDirection direction) {
            return std::array<T, directionsCount>::operator[](static_cast<size_t>(direction));
        }
    };

    class TGeoHashDescriptor {
    public:
        TGeoHashDescriptor() noexcept
            : Bits(0)
            , Steps(0)
        {
        }

        TGeoHashDescriptor(ui64 bits, ui8 steps) noexcept
            : Bits(bits)
            , Steps(steps)
        {
        }

        TGeoHashDescriptor(double latitude, double longitude, ui8 steps);
        TGeoHashDescriptor(double latitude, double longitude, const TBoundingBoxLL& limits, ui8 steps);
        TGeoHashDescriptor(const NGeo::TPointLL& point, ui8 steps);
        TGeoHashDescriptor(const NGeo::TPointLL& point, const TBoundingBoxLL& limits, ui8 steps);

        explicit TGeoHashDescriptor(const TString& hashString);

        ui64 GetBits() const;
        ui8 GetSteps() const;

        TString ToString() const;

        NGeo::TPointLL ToPoint(const TBoundingBoxLL& limits) const;
        NGeo::TPointLL ToPoint() const;

        TBoundingBoxLL ToBoundingBox(const TBoundingBoxLL& limits) const;
        TBoundingBoxLL ToBoundingBox() const;

        TMaybe<TGeoHashDescriptor> GetNeighbour(EDirection direction) const;
        TNeighbours<TMaybe<TGeoHashDescriptor>> GetNeighbours() const;

        TVector<TGeoHashDescriptor> GetChildren(ui8 steps) const;

        static ui8 StepsToPrecision(ui8 steps);
        static ui8 PrecisionToSteps(ui8 precision);

    private:
        void InitFromLatLon(double latitude, double longitude, const TBoundingBoxLL& limits, ui8 steps);
        std::pair<ui8, ui8> LatLonSteps() const;
        std::pair<ui32, ui32> LatLonBits() const;
        void SetLatLonBits(ui32 latBits, ui32 lonBits);
        static ui64 Interleave64(ui32 x, ui32 y);
        static std::pair<ui32, ui32> Deinterleave64(ui64 interleaved);

    private:
        static const ui8 StepsPerPrecisionUnit = 5;
        ui64 Bits;
        ui8 Steps;
    };

    ui64 Encode(double latitude, double longitude, ui8 precision);
    ui64 Encode(const NGeo::TPointLL& point, ui8 precision);

    TString EncodeToString(double latitude, double longitude, ui8 precision);
    TString EncodeToString(const NGeo::TPointLL& point, ui8 precision);

    NGeo::TPointLL DecodeToPoint(const TString& hashString);
    NGeo::TPointLL DecodeToPoint(ui64 hash, ui8 precision);

    TBoundingBoxLL DecodeToBoundingBox(const TString& hashString);
    TBoundingBoxLL DecodeToBoundingBox(ui64 hash, ui8 precision);

    TMaybe<ui64> GetNeighbour(ui64 hash, EDirection direction, ui8 precision);
    TMaybe<TString> GetNeighbour(const TString& hashString, EDirection direction);

    using TGeoHashBitsNeighbours = TNeighbours<TMaybe<ui64>>;
    using TGeoHashStringNeighbours = TNeighbours<TMaybe<TString>>;

    TGeoHashBitsNeighbours GetNeighbours(ui64 hash, ui8 precision);
    TGeoHashStringNeighbours GetNeighbours(const TString& hashString);

    TVector<TString> GetChildren(const TString& hashString);

} /* namespace NGeoHash */