diff options
author | Arne Juul <arnej@verizonmedia.com> | 2020-07-13 12:30:24 +0000 |
---|---|---|
committer | Arne Juul <arnej@verizonmedia.com> | 2020-07-15 15:39:23 +0000 |
commit | 909c3b8f302cbe615d38139d202e809c9f6079a4 (patch) | |
tree | f53d49c4a7a0a26f7ae250d9f47e8d3da5c584b1 /searchlib | |
parent | c1fb9ece30ea0bc233cf1b8a3ddd310dc269e111 (diff) |
immutable GeoLocation struct
Diffstat (limited to 'searchlib')
-rw-r--r-- | searchlib/src/vespa/searchlib/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | searchlib/src/vespa/searchlib/common/geo_location.cpp | 174 | ||||
-rw-r--r-- | searchlib/src/vespa/searchlib/common/geo_location.h | 89 |
3 files changed, 264 insertions, 0 deletions
diff --git a/searchlib/src/vespa/searchlib/common/CMakeLists.txt b/searchlib/src/vespa/searchlib/common/CMakeLists.txt index fd99f31aec8..f3b891bb55a 100644 --- a/searchlib/src/vespa/searchlib/common/CMakeLists.txt +++ b/searchlib/src/vespa/searchlib/common/CMakeLists.txt @@ -12,6 +12,7 @@ vespa_add_library(searchlib_common OBJECT featureset.cpp fileheadercontext.cpp gatecallback.cpp + geo_location.cpp geo_location_spec.cpp growablebitvector.cpp indexmetainfo.cpp diff --git a/searchlib/src/vespa/searchlib/common/geo_location.cpp b/searchlib/src/vespa/searchlib/common/geo_location.cpp new file mode 100644 index 00000000000..7f7aff55779 --- /dev/null +++ b/searchlib/src/vespa/searchlib/common/geo_location.cpp @@ -0,0 +1,174 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "geo_location.h" + +using vespalib::geo::ZCurve; + +namespace search::common { + +namespace { + +ZCurve::BoundingBox to_z(GeoLocation::Box box) { + return ZCurve::BoundingBox(box.x.lo, box.x.hi, + box.y.lo, box.y.hi); +} + +GeoLocation::Box +adjust_bounding_box(GeoLocation::Box orig, GeoLocation::Point point, uint32_t radius, GeoLocation::Aspect x_aspect) +{ + uint32_t maxdx = radius; + if (x_aspect.active()) { + uint64_t maxdx2 = ((static_cast<uint64_t>(radius) << 32) + 0xffffffffu) / x_aspect.multiplier; + if (maxdx2 >= 0xffffffffu) + maxdx = 0xffffffffu; + else + maxdx = static_cast<uint32_t>(maxdx2); + } + int64_t implied_max_x = int64_t(point.x) + int64_t(maxdx); + int64_t implied_min_x = int64_t(point.x) - int64_t(maxdx); + + int64_t implied_max_y = int64_t(point.y) + int64_t(radius); + int64_t implied_min_y = int64_t(point.y) - int64_t(radius); + + int32_t max_x = orig.x.hi; + int32_t min_x = orig.x.lo; + + int32_t max_y = orig.y.hi; + int32_t min_y = orig.y.lo; + + if (implied_max_x < max_x) max_x = implied_max_x; + if (implied_min_x > min_x) min_x = implied_min_x; + + if (implied_max_y < max_y) max_y = implied_max_y; + if (implied_min_y > min_y) min_y = implied_min_y; + + return GeoLocation::Box{GeoLocation::Range{min_x, max_x}, + GeoLocation::Range{min_y, max_y}}; +} + +} // namespace <unnamed> + +GeoLocation::GeoLocation() + : has_point(false), + point{0, 0}, + radius(radius_inf), + x_aspect(), + bounding_box(no_box), + _sq_radius(sq_radius_inf), + _zBoundingBox(0,0,0,0) +{} + +GeoLocation::GeoLocation(Point p) + : has_point(true), + point(p), + radius(radius_inf), + x_aspect(), + bounding_box(no_box), + _sq_radius(sq_radius_inf), + _zBoundingBox(0,0,0,0) +{} + +GeoLocation::GeoLocation(Point p, Aspect xa) + : has_point(true), + point(p), + radius(radius_inf), + x_aspect(xa), + bounding_box(no_box), + _sq_radius(sq_radius_inf), + _zBoundingBox(0,0,0,0) +{} + +GeoLocation::GeoLocation(Point p, uint32_t r) + : has_point(true), + point(p), + radius(r), + x_aspect(), + bounding_box(adjust_bounding_box(no_box, p, r, Aspect())), + _sq_radius(uint64_t(r) * uint64_t(r)), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Point p, uint32_t r, Aspect xa) + : has_point(true), + point(p), + radius(r), + x_aspect(), + bounding_box(adjust_bounding_box(no_box, p, r, xa)), + _sq_radius(uint64_t(r) * uint64_t(r)), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Box b) + : has_point(false), + point{0, 0}, + radius(radius_inf), + x_aspect(), + bounding_box(b), + _sq_radius(sq_radius_inf), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Box b, Point p) + : has_point(true), + point(p), + radius(radius_inf), + x_aspect(), + bounding_box(b), + _sq_radius(sq_radius_inf), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Box b, Point p, Aspect xa) + : has_point(true), + point(p), + radius(radius_inf), + x_aspect(xa), + bounding_box(b), + _sq_radius(sq_radius_inf), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Box b, Point p, uint32_t r) + : has_point(true), + point(p), + radius(r), + x_aspect(), + bounding_box(adjust_bounding_box(b, p, r, Aspect())), + _sq_radius(uint64_t(r) * uint64_t(r)), + _zBoundingBox(to_z(bounding_box)) +{} + +GeoLocation::GeoLocation(Box b, Point p, uint32_t r, Aspect xa) + : has_point(true), + point(p), + radius(r), + x_aspect(xa), + bounding_box(adjust_bounding_box(b, p, r, xa)), + _sq_radius(uint64_t(r) * uint64_t(r)), + _zBoundingBox(to_z(bounding_box)) +{} + +uint64_t GeoLocation::sq_distance_to(Point p) const { + if (has_point) { + uint64_t dx = (p.x > point.x) ? (p.x - point.x) : (point.x - p.x); + if (x_aspect.active()) { + dx = (dx * x_aspect.multiplier) >> 32; + } + uint64_t dy = (p.y > point.y) ? (p.y - point.y) : (point.y - p.y); + return dx*dx + dy*dy; + } + return 0; +} + +bool GeoLocation::inside_limit(Point p) const { + if (p.x < bounding_box.x.lo) return false; + if (p.x > bounding_box.x.hi) return false; + + if (p.y < bounding_box.y.lo) return false; + if (p.y > bounding_box.y.hi) return false; + + uint64_t sq_dist = sq_distance_to(p); + return sq_dist <= _sq_radius; +} + +} // namespace search::common diff --git a/searchlib/src/vespa/searchlib/common/geo_location.h b/searchlib/src/vespa/searchlib/common/geo_location.h new file mode 100644 index 00000000000..874b6d3470c --- /dev/null +++ b/searchlib/src/vespa/searchlib/common/geo_location.h @@ -0,0 +1,89 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <string> +#include <cstdint> +#include <limits> +#include <vespa/vespalib/geo/zcurve.h> + +namespace search::common { + +/** + * An immutable struct for a (geo) location. + * Contains a point with optional radius, a bounding box, or both. + **/ +struct GeoLocation +{ + // contained structs and helper constants: + static constexpr int32_t range_lo = std::numeric_limits<int32_t>::min(); + static constexpr int32_t range_hi = std::numeric_limits<int32_t>::max(); + static constexpr uint32_t radius_inf = std::numeric_limits<uint32_t>::max(); + struct Point { + const int32_t x; + const int32_t y; + Point() = delete; + }; + struct Aspect { + uint32_t multiplier; + Aspect() : multiplier(0) {} + bool active() const { return multiplier != 0; } + }; + struct Range { + const int32_t lo; + const int32_t hi; + bool active() const { + return (lo != range_lo) || (hi != range_hi); + } + }; + static constexpr Range no_range = {range_lo, range_hi}; + struct Box { + const Range x; + const Range y; + bool active() const { return x.active() || y.active(); } + }; + static constexpr Box no_box = {no_range, no_range}; + + // actual content of struct: + const bool has_point; + Point point; + uint32_t radius; + Aspect x_aspect; + Box bounding_box; + GeoLocation(); + + // constructors: + GeoLocation(Point p); + GeoLocation(Point p, Aspect xa); + GeoLocation(Point p, uint32_t r); + GeoLocation(Point p, uint32_t r, Aspect xa); + GeoLocation(Box b); + GeoLocation(Box b, Point p); + GeoLocation(Box b, Point p, Aspect xa); + GeoLocation(Box b, Point p, uint32_t r); + GeoLocation(Box b, Point p, uint32_t r, Aspect xa); + + // helper methods: + bool has_radius() const { return radius != radius_inf; } + bool valid() const { return has_point || bounding_box.active(); } + bool can_limit() const { return bounding_box.active(); } + + uint64_t sq_distance_to(Point p) const; + bool inside_limit(Point p) const; + + bool inside_limit(int64_t zcurve_encoded_xy) const { + if (_zBoundingBox.getzFailBoundingBoxTest(zcurve_encoded_xy)) return false; + int32_t x = 0; + int32_t y = 0; + vespalib::geo::ZCurve::decode(zcurve_encoded_xy, &x, &y); + return inside_limit(Point{x, y}); + } + +private: + // constants for implementation of helper methods: + static constexpr uint64_t sq_radius_inf = std::numeric_limits<uint64_t>::max(); + const uint64_t _sq_radius; + const vespalib::geo::ZCurve::BoundingBox _zBoundingBox; +}; + +} // namespace |