summaryrefslogtreecommitdiff
path: root/src/mongo/db/geo/hash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/geo/hash.cpp')
-rw-r--r--src/mongo/db/geo/hash.cpp105
1 files changed, 88 insertions, 17 deletions
diff --git a/src/mongo/db/geo/hash.cpp b/src/mongo/db/geo/hash.cpp
index 5f922ba4c73..5715c2b5eca 100644
--- a/src/mongo/db/geo/hash.cpp
+++ b/src/mongo/db/geo/hash.cpp
@@ -26,7 +26,7 @@
* it in the license file.
*/
-#include "mongo/pch.h"
+#include "mongo/db/field_parser.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/geo/core.h"
#include "mongo/db/geo/hash.h"
@@ -59,9 +59,13 @@ namespace mongo {
hashedToNormal[fixed] = i;
}
+ // Generate all 32 + 1 all-on bit patterns by repeatedly shifting the next bit to the
+ // correct position
+
long long currAllX = 0, currAllY = 0;
- for (int i = 0; i < 64; i++){
- long long thisBit = 1LL << (63 - i);
+ for (int i = 0; i < 64 + 2; i++){
+
+ long long thisBit = 1LL << (63 >= i ? 63 - i : 0);
if (i % 2 == 0) {
allX[i / 2] = currAllX;
@@ -79,12 +83,13 @@ namespace mongo {
// allX[1] = 8000000000000000
// allX[2] = a000000000000000
// allX[3] = a800000000000000
- long long allX[32];
+ // Note that 32 + 1 entries are needed, since 0 and 32 are both valid numbers of bits.
+ long long allX[33];
// Same alternating bits but starting with one from the MSB:
// allY[1] = 4000000000000000
// allY[2] = 5000000000000000
// allY[3] = 5400000000000000
- long long allY[32];
+ long long allY[33];
unsigned hashedToNormal[256];
};
@@ -105,6 +110,13 @@ namespace mongo {
return 1LL << (63 - i);
}
+ // Binary data is stored in some particular byte ordering that requires this.
+ static void copyAndReverse(char *dst, const char *src) {
+ for (unsigned a = 0; a < 8; a++) {
+ dst[a] = src[7 - a];
+ }
+ }
+
// Definition
unsigned int const GeoHash::kMaxBits = 32;
@@ -139,7 +151,7 @@ namespace mongo {
_bits = bits;
if (e.type() == BinData) {
int len = 0;
- _copyAndReverse((char*)&_hash, e.binData(len));
+ copyAndReverse((char*)&_hash, e.binData(len));
verify(len == 8);
} else {
cout << "GeoHash bad element: " << e << endl;
@@ -258,7 +270,7 @@ namespace mongo {
// TODO(hk): Comment this.
BSONObj GeoHash::wrap(const char* name) const {
BSONObjBuilder b(20);
- appendToBuilder(&b, name);
+ appendHashMin(&b, name);
BSONObj o = b.obj();
if ('\0' == name[0]) verify(o.objsize() == 20);
return o;
@@ -365,7 +377,12 @@ namespace mongo {
}
bool GeoHash::operator<(const GeoHash& h) const {
- if (_hash != h._hash) return _hash < h._hash;
+
+ if (_hash != h._hash) {
+ return static_cast<unsigned long long>(_hash) <
+ static_cast<unsigned long long>(h._hash);
+ }
+
return _bits < h._bits;
}
@@ -410,11 +427,25 @@ namespace mongo {
_hash &= mask;
}
- // Append the hash to the builder provided.
- void GeoHash::appendToBuilder(BSONObjBuilder* b, const char * name) const {
+ static void appendHashToBuilder(long long hash,
+ BSONObjBuilder* builder,
+ const char* fieldName) {
char buf[8];
- _copyAndReverse(buf, (char*)&_hash);
- b->appendBinData(name, 8, bdtCustom, buf);
+ copyAndReverse(buf, (char*) &hash);
+ builder->appendBinData(fieldName, 8, bdtCustom, buf);
+ }
+
+ void GeoHash::appendHashMin(BSONObjBuilder* builder, const char* fieldName) const {
+ // The min bound of a GeoHash region has all the unused suffix bits set to 0
+ appendHashToBuilder(_hash, builder, fieldName);
+ }
+
+ void GeoHash::appendHashMax(BSONObjBuilder* builder, const char* fieldName) const {
+ // The max bound of a GeoHash region has all the unused suffix bits set to 1
+ long long suffixMax = ~(geoBitSets.allX[_bits] | geoBitSets.allY[_bits]);
+ long long hashMax = _hash | suffixMax;
+
+ appendHashToBuilder(hashMax, builder, fieldName);
}
long long GeoHash::getHash() const {
@@ -464,14 +495,54 @@ namespace mongo {
return GeoHash(_hash, _bits - 1);
}
- // Binary data is stored in some particular byte ordering that requires this.
- void GeoHash::_copyAndReverse(char *dst, const char *src) {
- for (unsigned a = 0; a < 8; a++) {
- dst[a] = src[7 - a];
+ static BSONField<int> bitsField("bits", 26);
+ static BSONField<double> maxField("max", 180.0);
+ static BSONField<double> minField("min", -180.0);
+
+ Status GeoHashConverter::parseParameters(const BSONObj& paramDoc,
+ GeoHashConverter::Parameters* params) {
+
+ string errMsg;
+
+ if (FieldParser::FIELD_INVALID
+ == FieldParser::extractNumber(paramDoc, bitsField, &params->bits, &errMsg)) {
+ return Status(ErrorCodes::InvalidOptions, errMsg);
+ }
+
+ if (FieldParser::FIELD_INVALID
+ == FieldParser::extractNumber(paramDoc, maxField, &params->max, &errMsg)) {
+ return Status(ErrorCodes::InvalidOptions, errMsg);
+ }
+
+ if (FieldParser::FIELD_INVALID
+ == FieldParser::extractNumber(paramDoc, minField, &params->min, &errMsg)) {
+ return Status(ErrorCodes::InvalidOptions, errMsg);
}
+
+ if (params->bits < 1 || params->bits > 32) {
+ return Status(ErrorCodes::InvalidOptions,
+ str::stream() << "bits for hash must be > 0 and <= 32, "
+ << "but " << params->bits << " bits were specified");
+ }
+
+ if (params->min >= params->max) {
+ return Status(ErrorCodes::InvalidOptions,
+ str::stream() << "region for hash must be valid and have positive area, "
+ << "but [" << params->min << ", " << params->max << "]"
+ << "was specified");
+ }
+
+ double numBuckets = (1024 * 1024 * 1024 * 4.0);
+ params->scaling = numBuckets / (params->max - params->min);
+
+ return Status::OK();
+ }
+
+ GeoHashConverter::GeoHashConverter(const Parameters& params) : _params(params) {
+ init();
}
- GeoHashConverter::GeoHashConverter(const Parameters &params) : _params(params) {
+ void GeoHashConverter::init() {
// TODO(hk): What do we require of the values in params?
// Compute how much error there is so it can be used as a fudge factor.