2 * Copyright (c) 2018, Bertold Van den Bergh (vandenbergh@bertold.org)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the author nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #if defined(_MSC_VER) || defined(__MINGW32__)
34 #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
41 #include "zonedetect.h"
43 enum ZDInternalError {
48 #if defined(_MSC_VER) || defined(__MINGW32__)
50 ZD_E_DB_MAP_EXCEPTION,
51 ZD_E_DB_MUNMAP_MSVIEW,
58 struct ZoneDetectOpaque {
59 #if defined(_MSC_VER) || defined(__MINGW32__)
64 #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
83 uint32_t metadataOffset;
87 static void (*zdErrorHandler)(int, int);
88 static void zdError(enum ZDInternalError errZD, int errNative)
90 if (zdErrorHandler) zdErrorHandler((int)errZD, errNative);
93 static int32_t ZDFloatToFixedPoint(float input, float scale, unsigned int precision)
95 const float inputScaled = input / scale;
96 return (int32_t)(inputScaled * (float)(1 << (precision - 1)));
99 static float ZDFixedPointToFloat(int32_t input, float scale, unsigned int precision)
101 const float value = (float)input / (float)(1 << (precision - 1));
102 return value * scale;
105 static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, uint32_t *index, uint64_t *result)
107 if(*index >= (uint32_t)library->length) {
113 #if defined(_MSC_VER)
116 uint8_t *const buffer = library->mapping + *index;
117 uint8_t *const bufferEnd = library->mapping + library->length - 1;
119 unsigned int shift = 0;
121 value |= ((((uint64_t)buffer[i]) & UINT8_C(0x7F)) << shift);
124 if(!(buffer[i] & UINT8_C(0x80))) {
129 if(buffer + i > bufferEnd) {
133 #if defined(_MSC_VER)
134 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
135 ? EXCEPTION_EXECUTE_HANDLER
136 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
137 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
148 static unsigned int ZDDecodeVariableLengthUnsignedReverse(const ZoneDetect *library, uint32_t *index, uint64_t *result)
152 if(*index >= (uint32_t)library->length) {
156 #if defined(_MSC_VER)
160 if(library->mapping[i] & UINT8_C(0x80)) {
169 while(library->mapping[i] & UINT8_C(0x80)) {
176 #if defined(_MSC_VER)
177 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
178 ? EXCEPTION_EXECUTE_HANDLER
179 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
180 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
190 return ZDDecodeVariableLengthUnsigned(library, &i2, result);
193 static int64_t ZDDecodeUnsignedToSigned(uint64_t value)
195 return (value & 1) ? -(int64_t)(value / 2) : (int64_t)(value / 2);
198 static unsigned int ZDDecodeVariableLengthSigned(const ZoneDetect *library, uint32_t *index, int32_t *result)
201 const unsigned int retVal = ZDDecodeVariableLengthUnsigned(library, index, &value);
202 *result = (int32_t)ZDDecodeUnsignedToSigned(value);
206 static char *ZDParseString(const ZoneDetect *library, uint32_t *index)
209 if(!ZDDecodeVariableLengthUnsigned(library, index, &strLength)) {
213 uint32_t strOffset = *index;
214 unsigned int remoteStr = 0;
215 if(strLength >= 256) {
216 strOffset = library->metadataOffset + (uint32_t)strLength - 256;
219 if(!ZDDecodeVariableLengthUnsigned(library, &strOffset, &strLength)) {
223 if(strLength > 256) {
228 char *const str = static_cast<char *>(malloc((size_t)(strLength + 1)));
231 #if defined(_MSC_VER)
235 for(i = 0; i < strLength; i++) {
236 str[i] = (char)(library->mapping[strOffset + i] ^ UINT8_C(0x80));
238 #if defined(_MSC_VER)
239 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
240 ? EXCEPTION_EXECUTE_HANDLER
241 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
242 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
250 *index += (uint32_t)strLength;
256 static int ZDParseHeader(ZoneDetect *library)
258 if(library->length < 7) {
262 #if defined(_MSC_VER)
265 if(memcmp(library->mapping, "PLB", 3)) {
269 library->tableType = library->mapping[3];
270 library->version = library->mapping[4];
271 library->precision = library->mapping[5];
272 library->numFields = library->mapping[6];
273 #if defined(_MSC_VER)
274 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
275 ? EXCEPTION_EXECUTE_HANDLER
276 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
277 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
282 if(library->version >= 2) {
286 uint32_t index = UINT32_C(7);
288 library->fieldNames = static_cast<char **>(malloc(library->numFields * sizeof *library->fieldNames));
289 if (!library->fieldNames) {
294 for(i = 0; i < library->numFields; i++) {
295 library->fieldNames[i] = ZDParseString(library, &index);
298 library->notice = ZDParseString(library, &index);
299 if(!library->notice) {
304 /* Read section sizes */
305 /* By memset: library->bboxOffset = 0 */
307 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
308 library->metadataOffset = (uint32_t)tmp + library->bboxOffset;
310 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp))return -1;
311 library->dataOffset = (uint32_t)tmp + library->metadataOffset;
313 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
315 /* Add header size to everything */
316 library->bboxOffset += index;
317 library->metadataOffset += index;
318 library->dataOffset += index;
320 /* Verify file length */
321 if(tmp + library->dataOffset != (uint32_t)library->length) {
328 static int ZDPointInBox(int32_t xl, int32_t x, int32_t xr, int32_t yl, int32_t y, int32_t yr)
330 if((xl <= x && x <= xr) || (xr <= x && x <= xl)) {
331 if((yl <= y && y <= yr) || (yr <= y && y <= yl)) {
339 static uint32_t ZDUnshuffle(uint64_t w)
341 w &= 0x5555555555555555llu;
342 w = (w | (w >> 1)) & 0x3333333333333333llu;
343 w = (w | (w >> 2)) & 0x0F0F0F0F0F0F0F0Fllu;
344 w = (w | (w >> 4)) & 0x00FF00FF00FF00FFllu;
345 w = (w | (w >> 8)) & 0x0000FFFF0000FFFFllu;
346 w = (w | (w >> 16)) & 0x00000000FFFFFFFFllu;
350 static void ZDDecodePoint(uint64_t point, int32_t* lat, int32_t* lon)
352 *lat = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point));
353 *lon = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point >> 1));
357 const ZoneDetect *library;
358 uint32_t polygonIndex;
360 uint64_t numVertices;
363 uint32_t referenceStart, referenceEnd;
364 int32_t referenceDirection;
366 int32_t pointLat, pointLon;
367 int32_t firstLat, firstLon;
370 static void ZDReaderInit(struct Reader *reader, const ZoneDetect *library, uint32_t polygonIndex)
372 memset(reader, 0, sizeof(*reader));
374 reader->library = library;
375 reader->polygonIndex = polygonIndex;
380 static int ZDReaderGetPoint(struct Reader *reader, int32_t *pointLat, int32_t *pointLon)
382 int32_t diffLat = 0, diffLon = 0;
385 if(reader->done > 1) {
389 if(reader->first && reader->library->version == 0) {
390 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &reader->numVertices)) return -1;
391 if(!reader->numVertices) return -1;
394 uint8_t referenceDone = 0;
396 if(reader->library->version == 1) {
399 if(!reader->referenceDirection) {
400 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &point)) return -1;
402 if(reader->referenceDirection > 0) {
403 /* Read reference forward */
404 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->referenceStart, &point)) return -1;
405 if(reader->referenceStart >= reader->referenceEnd) {
408 } else if(reader->referenceDirection < 0) {
409 /* Read reference backwards */
410 if(!ZDDecodeVariableLengthUnsignedReverse(reader->library, &reader->referenceStart, &point)) return -1;
411 if(reader->referenceStart <= reader->referenceEnd) {
418 /* This is a special marker, it is not allowed in reference mode */
419 if(reader->referenceDirection) {
424 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &value)) return -1;
428 } else if(value == 1) {
431 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, (uint64_t*)&start)) return -1;
432 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diff)) return -1;
434 reader->referenceStart = reader->library->dataOffset+(uint32_t)start;
435 reader->referenceEnd = reader->library->dataOffset+(uint32_t)(start + diff);
436 reader->referenceDirection = diff;
438 reader->referenceStart--;
439 reader->referenceEnd--;
444 ZDDecodePoint(point, &diffLat, &diffLon);
445 if(reader->referenceDirection < 0) {
452 if(reader->library->version == 0) {
453 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLat)) return -1;
454 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLon)) return -1;
458 reader->pointLat += diffLat;
459 reader->pointLon += diffLon;
461 reader->firstLat = reader->pointLat;
462 reader->firstLon = reader->pointLon;
465 /* Close the polygon (the closing point is not encoded) */
466 reader->pointLat = reader->firstLat;
467 reader->pointLon = reader->firstLon;
473 if(reader->library->version == 0) {
474 reader->numVertices--;
475 if(!reader->numVertices) {
478 if(!diffLat && !diffLon) {
484 reader->referenceDirection = 0;
488 *pointLat = reader->pointLat;
492 *pointLon = reader->pointLon;
498 static int ZDFindPolygon(const ZoneDetect *library, uint32_t wantedId, uint32_t* metadataIndexPtr, uint32_t* polygonIndexPtr)
500 uint32_t polygonId = 0;
501 uint32_t bboxIndex = library->bboxOffset;
503 uint32_t metadataIndex = 0, polygonIndex = 0;
505 while(bboxIndex < library->metadataOffset) {
506 uint64_t polygonIndexDelta;
507 int32_t metadataIndexDelta;
509 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
510 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
511 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
512 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
513 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break;
514 if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
516 metadataIndex += (uint32_t)metadataIndexDelta;
517 polygonIndex += (uint32_t)polygonIndexDelta;
519 if(polygonId == wantedId) {
520 if(metadataIndexPtr) {
521 metadataIndex += library->metadataOffset;
522 *metadataIndexPtr = metadataIndex;
524 if(polygonIndexPtr) {
525 polygonIndex += library->dataOffset;
526 *polygonIndexPtr = polygonIndex;
537 static int32_t* ZDPolygonToListInternal(const ZoneDetect *library, uint32_t polygonIndex, size_t* length)
539 struct Reader reader;
540 ZDReaderInit(&reader, library, polygonIndex);
542 size_t listLength = 2 * 100;
543 size_t listIndex = 0;
545 int32_t* list = static_cast<int32_t *>(malloc(sizeof(int32_t) * listLength));
551 int32_t pointLat, pointLon;
552 int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
555 } else if(result == 0) {
559 if(listIndex >= listLength) {
561 if(listLength >= 1048576) {
565 list = static_cast<int32_t *>(realloc(list, sizeof(int32_t) * listLength));
571 list[listIndex++] = pointLat;
572 list[listIndex++] = pointLon;
588 float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* lengthPtr)
590 uint32_t polygonIndex;
591 int32_t* data = NULL;
592 float* flData = NULL;
595 if(!ZDFindPolygon(library, polygonId, NULL, &polygonIndex)) {
599 data = ZDPolygonToListInternal(library, polygonIndex, &length);
605 flData = static_cast<float *>(malloc(sizeof(float) * length));
610 for(size_t i = 0; i<length; i+= 2) {
611 int32_t lat = data[i];
612 int32_t lon = data[i+1];
614 flData[i] = ZDFixedPointToFloat(lat, 90, library->precision);
615 flData[i+1] = ZDFixedPointToFloat(lon, 180, library->precision);
634 static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polygonIndex, int32_t latFixedPoint, int32_t lonFixedPoint, uint64_t *distanceSqrMin)
636 int32_t pointLat, pointLon, prevLat = 0, prevLon = 0;
637 int prevQuadrant = 0, winding = 0;
641 struct Reader reader;
642 ZDReaderInit(&reader, library, polygonIndex);
645 int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
647 return ZD_LOOKUP_PARSE_ERROR;
648 } else if(result == 0) {
652 /* Check if point is ON the border */
653 if(pointLat == latFixedPoint && pointLon == lonFixedPoint) {
654 if(distanceSqrMin) *distanceSqrMin = 0;
655 return ZD_LOOKUP_ON_BORDER_VERTEX;
660 if(pointLat >= latFixedPoint) {
661 if(pointLon >= lonFixedPoint) {
667 if(pointLon >= lonFixedPoint) {
675 int windingNeedCompare = 0, lineIsStraight = 0;
678 /* Calculate winding number */
679 if(quadrant == prevQuadrant) {
681 } else if(quadrant == (prevQuadrant + 1) % 4) {
683 } else if((quadrant + 1) % 4 == prevQuadrant) {
686 windingNeedCompare = 1;
689 /* Avoid horizontal and vertical lines */
690 if((pointLon == prevLon || pointLat == prevLat)) {
694 /* Calculate the parameters of y=ax+b if needed */
695 if(!lineIsStraight && (distanceSqrMin || windingNeedCompare)) {
696 a = ((float)pointLat - (float)prevLat) / ((float)pointLon - (float)prevLon);
697 b = (float)pointLat - a * (float)pointLon;
700 int onStraight = ZDPointInBox(pointLat, latFixedPoint, prevLat, pointLon, lonFixedPoint, prevLon);
701 if(lineIsStraight && (onStraight || windingNeedCompare)) {
702 if(distanceSqrMin) *distanceSqrMin = 0;
703 return ZD_LOOKUP_ON_BORDER_SEGMENT;
706 /* Jumped two quadrants. */
707 if(windingNeedCompare) {
708 /* Check if the target is on the border */
709 const int32_t intersectLon = (int32_t)(((float)latFixedPoint - b) / a);
710 if(intersectLon >= lonFixedPoint-1 && intersectLon <= lonFixedPoint+1) {
711 if(distanceSqrMin) *distanceSqrMin = 0;
712 return ZD_LOOKUP_ON_BORDER_SEGMENT;
715 /* Ok, it's not. In which direction did we go round the target? */
716 const int sign = (intersectLon < lonFixedPoint) ? 2 : -2;
717 if(quadrant == 2 || quadrant == 3) {
724 /* Calculate closest point on line (if needed) */
726 float closestLon, closestLat;
727 if(!lineIsStraight) {
728 closestLon = ((float)lonFixedPoint + a * (float)latFixedPoint - a * b) / (a * a + 1);
729 closestLat = (a * ((float)lonFixedPoint + a * (float)latFixedPoint) + b) / (a * a + 1);
731 if(pointLon == prevLon) {
732 closestLon = (float)pointLon;
733 closestLat = (float)latFixedPoint;
735 closestLon = (float)lonFixedPoint;
736 closestLat = (float)pointLat;
740 const int closestInBox = ZDPointInBox(pointLon, (int32_t)closestLon, prevLon, pointLat, (int32_t)closestLat, prevLat);
742 int64_t diffLat, diffLon;
744 /* Calculate squared distance to segment. */
745 diffLat = (int64_t)(closestLat - (float)latFixedPoint);
746 diffLon = (int64_t)(closestLon - (float)lonFixedPoint);
749 * Calculate squared distance to vertices
750 * It is enough to check the current point since the polygon is closed.
752 diffLat = (int64_t)(pointLat - latFixedPoint);
753 diffLon = (int64_t)(pointLon - lonFixedPoint);
756 /* Note: lon has half scale */
757 uint64_t distanceSqr = (uint64_t)(diffLat * diffLat) + (uint64_t)(diffLon * diffLon) * 4;
758 if(distanceSqr < *distanceSqrMin) *distanceSqrMin = distanceSqr;
762 prevQuadrant = quadrant;
769 return ZD_LOOKUP_IN_ZONE;
770 } else if(winding == 4) {
771 return ZD_LOOKUP_IN_EXCLUDED_ZONE;
772 } else if(winding == 0) {
773 return ZD_LOOKUP_NOT_IN_ZONE;
776 /* Should not happen */
777 if(distanceSqrMin) *distanceSqrMin = 0;
778 return ZD_LOOKUP_ON_BORDER_SEGMENT;
781 void ZDCloseDatabase(ZoneDetect *library)
784 if(library->fieldNames) {
786 for(i = 0; i < (size_t)library->numFields; i++) {
787 if(library->fieldNames[i]) {
788 free(library->fieldNames[i]);
791 free(library->fieldNames);
793 if(library->notice) {
794 free(library->notice);
797 if(library->closeType == 0) {
798 #if defined(_MSC_VER) || defined(__MINGW32__)
799 if(library->mapping && !UnmapViewOfFile(library->mapping)) zdError(ZD_E_DB_MUNMAP_MSVIEW, (int)GetLastError());
800 if(library->fdMap && !CloseHandle(library->fdMap)) zdError(ZD_E_DB_MUNMAP, (int)GetLastError());
801 if(library->fd && !CloseHandle(library->fd)) zdError(ZD_E_DB_CLOSE, (int)GetLastError());
802 #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
803 if(library->mapping && munmap(library->mapping, (size_t)(library->length))) zdError(ZD_E_DB_MUNMAP, 0);
804 if(library->fd >= 0 && close(library->fd)) zdError(ZD_E_DB_CLOSE, 0);
812 ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length)
814 ZoneDetect *const library = static_cast<ZoneDetect *>(malloc(sizeof *library));
817 memset(library, 0, sizeof(*library));
818 library->closeType = 1;
819 library->length = (long int)length;
821 if(library->length <= 0) {
822 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
823 zdError(ZD_E_DB_SEEK, errno);
825 zdError(ZD_E_DB_SEEK, 0);
830 library->mapping = static_cast<uint8_t *>(buffer);
832 /* Parse the header */
833 if(ZDParseHeader(library)) {
834 zdError(ZD_E_PARSE_HEADER, 0);
842 ZDCloseDatabase(library);
846 ZoneDetect *ZDOpenDatabase(const char *path)
848 ZoneDetect *const library = static_cast<ZoneDetect *>(malloc(sizeof *library));
851 memset(library, 0, sizeof(*library));
853 #if defined(_MSC_VER) || defined(__MINGW32__)
854 library->fd = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
855 if (library->fd == INVALID_HANDLE_VALUE) {
856 zdError(ZD_E_DB_OPEN, (int)GetLastError());
860 const DWORD fsize = GetFileSize(library->fd, NULL);
861 if (fsize == INVALID_FILE_SIZE) {
862 zdError(ZD_E_DB_SEEK, (int)GetLastError());
865 library->length = (int32_t)fsize;
867 library->fdMap = CreateFileMappingA(library->fd, NULL, PAGE_READONLY, 0, 0, NULL);
868 if (!library->fdMap) {
869 zdError(ZD_E_DB_MMAP, (int)GetLastError());
873 library->mapping = MapViewOfFile(library->fdMap, FILE_MAP_READ, 0, 0, 0);
874 if (!library->mapping) {
875 zdError(ZD_E_DB_MMAP_MSVIEW, (int)GetLastError());
878 #elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
879 library->fd = open(path, O_RDONLY | O_CLOEXEC);
880 if(library->fd < 0) {
881 zdError(ZD_E_DB_OPEN, errno);
885 library->length = lseek(library->fd, 0, SEEK_END);
886 if(library->length <= 0 || library->length > 50331648) {
887 zdError(ZD_E_DB_SEEK, errno);
890 lseek(library->fd, 0, SEEK_SET);
892 library->mapping = static_cast<uint8_t *>(mmap(NULL, (size_t)library->length, PROT_READ, MAP_PRIVATE | MAP_FILE, library->fd, 0));
893 if(library->mapping == MAP_FAILED) {
894 zdError(ZD_E_DB_MMAP, errno);
899 /* Parse the header */
900 if(ZDParseHeader(library)) {
901 zdError(ZD_E_PARSE_HEADER, 0);
909 ZDCloseDatabase(library);
913 ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone)
915 const int32_t latFixedPoint = ZDFloatToFixedPoint(lat, 90, library->precision);
916 const int32_t lonFixedPoint = ZDFloatToFixedPoint(lon, 180, library->precision);
917 size_t numResults = 0;
918 uint64_t distanceSqrMin = (uint64_t)-1;
920 /* Iterate over all polygons */
921 uint32_t bboxIndex = library->bboxOffset;
922 uint32_t metadataIndex = 0;
923 uint32_t polygonIndex = 0;
925 ZoneDetectResult *results = static_cast<ZoneDetectResult *>(malloc(sizeof *results));
930 uint32_t polygonId = 0;
932 while(bboxIndex < library->metadataOffset) {
933 int32_t minLat, minLon, maxLat, maxLon, metadataIndexDelta;
934 uint64_t polygonIndexDelta;
935 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLat)) break;
936 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLon)) break;
937 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLat)) break;
938 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLon)) break;
939 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break;
940 if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
942 metadataIndex += (uint32_t)metadataIndexDelta;
943 polygonIndex += (uint32_t)polygonIndexDelta;
945 if(latFixedPoint >= minLat) {
946 if(latFixedPoint <= maxLat &&
947 lonFixedPoint >= minLon &&
948 lonFixedPoint <= maxLon) {
950 const ZDLookupResult lookupResult = ZDPointInPolygon(library, library->dataOffset + polygonIndex, latFixedPoint, lonFixedPoint, (safezone) ? &distanceSqrMin : NULL);
951 if(lookupResult == ZD_LOOKUP_PARSE_ERROR) {
953 } else if(lookupResult != ZD_LOOKUP_NOT_IN_ZONE) {
954 ZoneDetectResult *const newResults = static_cast<ZoneDetectResult *>(realloc(results, sizeof *newResults * (numResults + 2)));
957 results = newResults;
958 results[numResults].polygonId = polygonId;
959 results[numResults].metaId = metadataIndex;
960 results[numResults].numFields = library->numFields;
961 results[numResults].fieldNames = library->fieldNames;
962 results[numResults].lookupResult = lookupResult;
971 /* The data is sorted along minLat */
978 /* Clean up results */
980 for(i = 0; i < numResults; i++) {
982 ZDLookupResult overrideResult = ZD_LOOKUP_IGNORE;
984 for(j = i; j < numResults; j++) {
985 if(results[i].metaId == results[j].metaId) {
986 ZDLookupResult tmpResult = results[j].lookupResult;
987 results[j].lookupResult = ZD_LOOKUP_IGNORE;
989 /* This is the same result. Is it an exclusion zone? */
990 if(tmpResult == ZD_LOOKUP_IN_ZONE) {
992 } else if(tmpResult == ZD_LOOKUP_IN_EXCLUDED_ZONE) {
995 /* If on the bodrder then the final result is on the border */
996 overrideResult = tmpResult;
1002 if(overrideResult != ZD_LOOKUP_IGNORE) {
1003 results[i].lookupResult = overrideResult;
1006 results[i].lookupResult = ZD_LOOKUP_IN_ZONE;
1011 /* Remove zones to ignore */
1012 size_t newNumResults = 0;
1013 for(i = 0; i < numResults; i++) {
1014 if(results[i].lookupResult != ZD_LOOKUP_IGNORE) {
1015 results[newNumResults] = results[i];
1019 numResults = newNumResults;
1021 /* Lookup metadata */
1022 for(i = 0; i < numResults; i++) {
1023 uint32_t tmpIndex = library->metadataOffset + results[i].metaId;
1024 results[i].data = static_cast<char **>(malloc(library->numFields * sizeof *results[i].data));
1025 if(results[i].data) {
1027 for(j = 0; j < library->numFields; j++) {
1028 results[i].data[j] = ZDParseString(library, &tmpIndex);
1029 if (!results[i].data[j]) {
1030 /* free all allocated memory */
1032 for(m = 0; m < j; m++) {
1033 if(results[i].data[m]) {
1034 free(results[i].data[m]);
1038 for(k = 0; k < i; k++) {
1040 for(l = 0; l < (size_t)results[k].numFields; l++) {
1041 if(results[k].data[l]) {
1042 free(results[k].data[l]);
1045 if (results[k].data) {
1046 free(results[k].data);
1055 /* free all allocated memory */
1057 for(k = 0; k < i; k++) {
1059 for(l = 0; l < (size_t)results[k].numFields; l++) {
1060 if(results[k].data[l]) {
1061 free(results[k].data[l]);
1064 if (results[k].data) {
1065 free(results[k].data);
1073 /* Write end marker */
1074 results[numResults].lookupResult = ZD_LOOKUP_END;
1075 results[numResults].numFields = 0;
1076 results[numResults].fieldNames = NULL;
1077 results[numResults].data = NULL;
1080 *safezone = sqrtf((float)distanceSqrMin) * 90 / (float)(1 << (library->precision - 1));
1086 void ZDFreeResults(ZoneDetectResult *results)
1088 unsigned int index = 0;
1094 while(results[index].lookupResult != ZD_LOOKUP_END) {
1095 if(results[index].data) {
1097 for(i = 0; i < (size_t)results[index].numFields; i++) {
1098 if(results[index].data[i]) {
1099 free(results[index].data[i]);
1102 free(results[index].data);
1109 const char *ZDGetNotice(const ZoneDetect *library)
1111 return library->notice;
1114 uint8_t ZDGetTableType(const ZoneDetect *library)
1116 return library->tableType;
1119 const char *ZDLookupResultToString(ZDLookupResult result)
1122 case ZD_LOOKUP_IGNORE:
1126 case ZD_LOOKUP_PARSE_ERROR:
1127 return "Parsing error";
1128 case ZD_LOOKUP_NOT_IN_ZONE:
1129 return "Not in zone";
1130 case ZD_LOOKUP_IN_ZONE:
1132 case ZD_LOOKUP_IN_EXCLUDED_ZONE:
1133 return "In excluded zone";
1134 case ZD_LOOKUP_ON_BORDER_VERTEX:
1135 return "Target point is border vertex";
1136 case ZD_LOOKUP_ON_BORDER_SEGMENT:
1137 return "Target point is on border";
1143 #define ZD_E_COULD_NOT(msg) "could not " msg
1145 const char *ZDGetErrorString(int errZD)
1147 switch ((enum ZDInternalError)errZD) {
1153 return ZD_E_COULD_NOT("open database file");
1155 return ZD_E_COULD_NOT("retrieve database file size");
1157 return ZD_E_COULD_NOT("map database file to system memory");
1158 #if defined(_MSC_VER) || defined(__MINGW32__)
1159 case ZD_E_DB_MMAP_MSVIEW :
1160 return ZD_E_COULD_NOT("open database file view");
1161 case ZD_E_DB_MAP_EXCEPTION:
1162 return "I/O exception occurred while accessing database file view";
1163 case ZD_E_DB_MUNMAP_MSVIEW:
1164 return ZD_E_COULD_NOT("close database file view");
1166 case ZD_E_DB_MUNMAP :
1167 return ZD_E_COULD_NOT("unmap database");
1168 case ZD_E_DB_CLOSE :
1169 return ZD_E_COULD_NOT("close database file");
1170 case ZD_E_PARSE_HEADER :
1171 return ZD_E_COULD_NOT("parse database header");
1175 #undef ZD_E_COULD_NOT
1177 int ZDSetErrorHandler(void (*handler)(int, int))
1179 zdErrorHandler = handler;
1183 char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon)
1185 ZoneDetectResult *result = ZDLookup(library, lat, lon, NULL);
1190 char* output = NULL;
1192 char* strings[2] = {NULL};
1194 if(result[0].lookupResult == ZD_LOOKUP_END) {
1198 for(int i = 0; i < result[0].numFields; i++) {
1199 if(result[0].fieldNames[i] && result[0].data[i]) {
1200 if(library->tableType == 'T') {
1201 if(!strcmp(result[0].fieldNames[i], "TimezoneIdPrefix")) {
1202 strings[0] = result[0].data[i];
1204 if(!strcmp(result[0].fieldNames[i], "TimezoneId")) {
1205 strings[1] = result[0].data[i];
1208 if(library->tableType == 'C') {
1209 if(!strcmp(result[0].fieldNames[i], "Name")) {
1210 strings[0] = result[0].data[i];
1216 for(size_t i=0; i<sizeof(strings)/sizeof(char*); i++) {
1218 size_t partLength = strlen(strings[i]);
1219 if(partLength > 512) {
1222 length += partLength;
1232 output = (char*)malloc(length);
1235 for(size_t i=0; i<sizeof(strings)/sizeof(char*); i++) {
1237 strcat(output + strlen(output), strings[i]);
1243 ZDFreeResults(result);