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.
35 #if defined(_MSC_VER) || defined(__MINGW32__)
44 #include "zonedetect.h"
46 enum ZDInternalError {
51 #if defined(_MSC_VER) || defined(__MINGW32__)
53 ZD_E_DB_MAP_EXCEPTION,
54 ZD_E_DB_MUNMAP_MSVIEW,
61 struct ZoneDetectOpaque {
62 #if defined(_MSC_VER) || defined(__MINGW32__)
84 uint32_t metadataOffset;
88 static void (*zdErrorHandler)(int, int);
89 static void zdError(enum ZDInternalError errZD, int errNative)
91 if (zdErrorHandler) zdErrorHandler((int)errZD, errNative);
94 static int32_t ZDFloatToFixedPoint(float input, float scale, unsigned int precision)
96 const float inputScaled = input / scale;
97 return (int32_t)(inputScaled * (float)(1 << (precision - 1)));
100 static float ZDFixedPointToFloat(int32_t input, float scale, unsigned int precision)
102 const float value = (float)input / (float)(1 << (precision - 1));
103 return value * scale;
106 static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, uint32_t *index, uint64_t *result)
108 if(*index >= (uint32_t)library->length) {
114 #if defined(_MSC_VER)
117 uint8_t *const buffer = library->mapping + *index;
118 uint8_t *const bufferEnd = library->mapping + library->length - 1;
120 unsigned int shift = 0;
122 value |= ((((uint64_t)buffer[i]) & UINT8_C(0x7F)) << shift);
125 if(!(buffer[i] & UINT8_C(0x80))) {
130 if(buffer + i > bufferEnd) {
134 #if defined(_MSC_VER)
135 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
136 ? EXCEPTION_EXECUTE_HANDLER
137 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
138 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
149 static unsigned int ZDDecodeVariableLengthUnsignedReverse(const ZoneDetect *library, uint32_t *index, uint64_t *result)
153 if(*index >= (uint32_t)library->length) {
157 #if defined(_MSC_VER)
161 if(library->mapping[i] & UINT8_C(0x80)) {
170 while(library->mapping[i] & UINT8_C(0x80)) {
177 #if defined(_MSC_VER)
178 } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
179 ? EXCEPTION_EXECUTE_HANDLER
180 : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
181 zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
191 return ZDDecodeVariableLengthUnsigned(library, &i2, result);
194 static int64_t ZDDecodeUnsignedToSigned(uint64_t value)
196 return (value & 1) ? -(int64_t)(value / 2) : (int64_t)(value / 2);
199 static unsigned int ZDDecodeVariableLengthSigned(const ZoneDetect *library, uint32_t *index, int32_t *result)
202 const unsigned int retVal = ZDDecodeVariableLengthUnsigned(library, index, &value);
203 *result = (int32_t)ZDDecodeUnsignedToSigned(value);
207 static char *ZDParseString(const ZoneDetect *library, uint32_t *index)
210 if(!ZDDecodeVariableLengthUnsigned(library, index, &strLength)) {
214 uint32_t strOffset = *index;
215 unsigned int remoteStr = 0;
216 if(strLength >= 256) {
217 strOffset = library->metadataOffset + (uint32_t)strLength - 256;
220 if(!ZDDecodeVariableLengthUnsigned(library, &strOffset, &strLength)) {
224 if(strLength > 256) {
229 char *const str = malloc((size_t)(strLength + 1));
232 #if defined(_MSC_VER)
235 for(size_t 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 = malloc(library->numFields * sizeof *library->fieldNames);
289 for(size_t i = 0; i < library->numFields; i++) {
290 library->fieldNames[i] = ZDParseString(library, &index);
293 library->notice = ZDParseString(library, &index);
294 if(!library->notice) {
299 /* Read section sizes */
300 /* By memset: library->bboxOffset = 0 */
302 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
303 library->metadataOffset = (uint32_t)tmp + library->bboxOffset;
305 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp))return -1;
306 library->dataOffset = (uint32_t)tmp + library->metadataOffset;
308 if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
310 /* Add header size to everything */
311 library->bboxOffset += index;
312 library->metadataOffset += index;
313 library->dataOffset += index;
315 /* Verify file length */
316 if(tmp + library->dataOffset != (uint32_t)library->length) {
323 static int ZDPointInBox(int32_t xl, int32_t x, int32_t xr, int32_t yl, int32_t y, int32_t yr)
325 if((xl <= x && x <= xr) || (xr <= x && x <= xl)) {
326 if((yl <= y && y <= yr) || (yr <= y && y <= yl)) {
334 static uint32_t ZDUnshuffle(uint64_t w)
336 w &= 0x5555555555555555;
337 w = (w | (w >> 1)) & 0x3333333333333333;
338 w = (w | (w >> 2)) & 0x0F0F0F0F0F0F0F0F;
339 w = (w | (w >> 4)) & 0x00FF00FF00FF00FF;
340 w = (w | (w >> 8)) & 0x0000FFFF0000FFFF;
341 w = (w | (w >> 16)) & 0x00000000FFFFFFFF;
345 static void ZDDecodePoint(uint64_t point, int32_t* lat, int32_t* lon)
347 *lat = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point));
348 *lon = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point >> 1));
352 const ZoneDetect *library;
353 uint32_t polygonIndex;
355 uint64_t numVertices;
358 uint32_t referenceStart, referenceEnd;
359 int32_t referenceDirection;
361 int32_t pointLat, pointLon;
362 int32_t firstLat, firstLon;
365 static void ZDReaderInit(struct Reader *reader, const ZoneDetect *library, uint32_t polygonIndex)
367 memset(reader, 0, sizeof(*reader));
369 reader->library = library;
370 reader->polygonIndex = polygonIndex;
375 static int ZDReaderGetPoint(struct Reader *reader, int32_t *pointLat, int32_t *pointLon)
377 int32_t diffLat = 0, diffLon = 0;
380 if(reader->done > 1) {
384 if(reader->first && reader->library->version == 0) {
385 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &reader->numVertices)) return -1;
386 if(!reader->numVertices) return -1;
389 uint8_t referenceDone = 0;
391 if(reader->library->version == 1) {
394 if(!reader->referenceDirection) {
395 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &point)) return -1;
397 if(reader->referenceDirection > 0) {
398 /* Read reference forward */
399 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->referenceStart, &point)) return -1;
400 if(reader->referenceStart >= reader->referenceEnd) {
403 } else if(reader->referenceDirection < 0) {
404 /* Read reference backwards */
405 if(!ZDDecodeVariableLengthUnsignedReverse(reader->library, &reader->referenceStart, &point)) return -1;
406 if(reader->referenceStart <= reader->referenceEnd) {
413 /* This is a special marker, it is not allowed in reference mode */
414 if(reader->referenceDirection) {
419 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &value)) return -1;
423 } else if(value == 1) {
426 if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, (uint64_t*)&start)) return -1;
427 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diff)) return -1;
429 reader->referenceStart = reader->library->dataOffset+(uint32_t)start;
430 reader->referenceEnd = reader->library->dataOffset+(uint32_t)(start + diff);
431 reader->referenceDirection = diff;
433 reader->referenceStart--;
434 reader->referenceEnd--;
439 ZDDecodePoint(point, &diffLat, &diffLon);
440 if(reader->referenceDirection < 0) {
447 if(reader->library->version == 0) {
448 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLat)) return -1;
449 if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLon)) return -1;
453 reader->pointLat += diffLat;
454 reader->pointLon += diffLon;
456 reader->firstLat = reader->pointLat;
457 reader->firstLon = reader->pointLon;
460 /* Close the polygon (the closing point is not encoded) */
461 reader->pointLat = reader->firstLat;
462 reader->pointLon = reader->firstLon;
468 if(reader->library->version == 0) {
469 reader->numVertices--;
470 if(!reader->numVertices) {
473 if(!diffLat && !diffLon) {
479 reader->referenceDirection = 0;
483 *pointLat = reader->pointLat;
487 *pointLon = reader->pointLon;
493 static int ZDFindPolygon(const ZoneDetect *library, uint32_t wantedId, uint32_t* metadataIndexPtr, uint32_t* polygonIndexPtr)
495 uint32_t polygonId = 0;
496 uint32_t bboxIndex = library->bboxOffset;
498 uint32_t metadataIndex = 0, polygonIndex = 0;
500 while(bboxIndex < library->metadataOffset) {
501 uint64_t polygonIndexDelta;
502 int32_t metadataIndexDelta;
504 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
505 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
506 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
507 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
508 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break;
509 if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
511 metadataIndex += (uint32_t)metadataIndexDelta;
512 polygonIndex += (uint32_t)polygonIndexDelta;
514 if(polygonId == wantedId) {
515 if(metadataIndexPtr) {
516 metadataIndex += library->metadataOffset;
517 *metadataIndexPtr = metadataIndex;
519 if(polygonIndexPtr) {
520 polygonIndex += library->dataOffset;
521 *polygonIndexPtr = polygonIndex;
532 static int32_t* ZDPolygonToListInternal(const ZoneDetect *library, uint32_t polygonIndex, size_t* length)
534 struct Reader reader;
535 ZDReaderInit(&reader, library, polygonIndex);
537 size_t listLength = 2 * 100;
538 size_t listIndex = 0;
540 int32_t* list = malloc(sizeof(int32_t) * listLength);
546 int32_t pointLat, pointLon;
547 int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
550 } else if(result == 0) {
554 if(listIndex >= listLength) {
556 if(listLength >= 1048576) {
560 list = realloc(list, sizeof(int32_t) * listLength);
566 list[listIndex++] = pointLat;
567 list[listIndex++] = pointLon;
583 float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* lengthPtr)
585 uint32_t polygonIndex;
586 int32_t* data = NULL;
587 float* flData = NULL;
589 if(!ZDFindPolygon(library, polygonId, NULL, &polygonIndex)) {
594 data = ZDPolygonToListInternal(library, polygonIndex, &length);
600 flData = malloc(sizeof(float) * length);
605 for(size_t i = 0; i<length; i+= 2) {
606 int32_t lat = data[i];
607 int32_t lon = data[i+1];
609 flData[i] = ZDFixedPointToFloat(lat, 90, library->precision);
610 flData[i+1] = ZDFixedPointToFloat(lon, 180, library->precision);
629 static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polygonIndex, int32_t latFixedPoint, int32_t lonFixedPoint, uint64_t *distanceSqrMin)
631 int32_t pointLat, pointLon, prevLat = 0, prevLon = 0;
632 int prevQuadrant = 0, winding = 0;
636 struct Reader reader;
637 ZDReaderInit(&reader, library, polygonIndex);
640 int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
642 return ZD_LOOKUP_PARSE_ERROR;
643 } else if(result == 0) {
647 /* Check if point is ON the border */
648 if(pointLat == latFixedPoint && pointLon == lonFixedPoint) {
649 if(distanceSqrMin) *distanceSqrMin = 0;
650 return ZD_LOOKUP_ON_BORDER_VERTEX;
655 if(pointLat >= latFixedPoint) {
656 if(pointLon >= lonFixedPoint) {
662 if(pointLon >= lonFixedPoint) {
670 int windingNeedCompare = 0, lineIsStraight = 0;
673 /* Calculate winding number */
674 if(quadrant == prevQuadrant) {
676 } else if(quadrant == (prevQuadrant + 1) % 4) {
678 } else if((quadrant + 1) % 4 == prevQuadrant) {
681 windingNeedCompare = 1;
684 /* Avoid horizontal and vertical lines */
685 if((pointLon == prevLon || pointLat == prevLat)) {
689 /* Calculate the parameters of y=ax+b if needed */
690 if(!lineIsStraight && (distanceSqrMin || windingNeedCompare)) {
691 a = ((float)pointLat - (float)prevLat) / ((float)pointLon - (float)prevLon);
692 b = (float)pointLat - a * (float)pointLon;
695 int onStraight = ZDPointInBox(pointLat, latFixedPoint, prevLat, pointLon, lonFixedPoint, prevLon);
696 if(lineIsStraight && (onStraight || windingNeedCompare)) {
697 if(distanceSqrMin) *distanceSqrMin = 0;
698 return ZD_LOOKUP_ON_BORDER_SEGMENT;
701 /* Jumped two quadrants. */
702 if(windingNeedCompare) {
703 /* Check if the target is on the border */
704 const int32_t intersectLon = (int32_t)(((float)latFixedPoint - b) / a);
705 if(intersectLon >= lonFixedPoint-1 && intersectLon <= lonFixedPoint+1) {
706 if(distanceSqrMin) *distanceSqrMin = 0;
707 return ZD_LOOKUP_ON_BORDER_SEGMENT;
710 /* Ok, it's not. In which direction did we go round the target? */
711 const int sign = (intersectLon < lonFixedPoint) ? 2 : -2;
712 if(quadrant == 2 || quadrant == 3) {
719 /* Calculate closest point on line (if needed) */
721 float closestLon, closestLat;
722 if(!lineIsStraight) {
723 closestLon = ((float)lonFixedPoint + a * (float)latFixedPoint - a * b) / (a * a + 1);
724 closestLat = (a * ((float)lonFixedPoint + a * (float)latFixedPoint) + b) / (a * a + 1);
726 if(pointLon == prevLon) {
727 closestLon = (float)pointLon;
728 closestLat = (float)latFixedPoint;
730 closestLon = (float)lonFixedPoint;
731 closestLat = (float)pointLat;
735 const int closestInBox = ZDPointInBox(pointLon, (int32_t)closestLon, prevLon, pointLat, (int32_t)closestLat, prevLat);
737 int64_t diffLat, diffLon;
739 /* Calculate squared distance to segment. */
740 diffLat = (int64_t)(closestLat - (float)latFixedPoint);
741 diffLon = (int64_t)(closestLon - (float)lonFixedPoint);
744 * Calculate squared distance to vertices
745 * It is enough to check the current point since the polygon is closed.
747 diffLat = (int64_t)(pointLat - latFixedPoint);
748 diffLon = (int64_t)(pointLon - lonFixedPoint);
751 /* Note: lon has half scale */
752 uint64_t distanceSqr = (uint64_t)(diffLat * diffLat) + (uint64_t)(diffLon * diffLon) * 4;
753 if(distanceSqr < *distanceSqrMin) *distanceSqrMin = distanceSqr;
757 prevQuadrant = quadrant;
764 return ZD_LOOKUP_IN_ZONE;
765 } else if(winding == 4) {
766 return ZD_LOOKUP_IN_EXCLUDED_ZONE;
767 } else if(winding == 0) {
768 return ZD_LOOKUP_NOT_IN_ZONE;
771 /* Should not happen */
772 if(distanceSqrMin) *distanceSqrMin = 0;
773 return ZD_LOOKUP_ON_BORDER_SEGMENT;
776 void ZDCloseDatabase(ZoneDetect *library)
779 if(library->fieldNames) {
780 for(size_t i = 0; i < (size_t)library->numFields; i++) {
781 if(library->fieldNames[i]) {
782 free(library->fieldNames[i]);
785 free(library->fieldNames);
787 if(library->notice) {
788 free(library->notice);
791 if(library->closeType == 0) {
792 #if defined(_MSC_VER) || defined(__MINGW32__)
793 if(library->mapping && !UnmapViewOfFile(library->mapping)) zdError(ZD_E_DB_MUNMAP_MSVIEW, (int)GetLastError());
794 if(library->fdMap && !CloseHandle(library->fdMap)) zdError(ZD_E_DB_MUNMAP, (int)GetLastError());
795 if(library->fd && !CloseHandle(library->fd)) zdError(ZD_E_DB_CLOSE, (int)GetLastError());
797 if(library->mapping && munmap(library->mapping, (size_t)(library->length))) zdError(ZD_E_DB_MUNMAP, errno);
798 if(library->fd >= 0 && close(library->fd)) zdError(ZD_E_DB_CLOSE, errno);
806 ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length)
808 ZoneDetect *const library = malloc(sizeof *library);
811 memset(library, 0, sizeof(*library));
812 library->closeType = 1;
813 library->length = (long int)length;
815 if(library->length <= 0) {
816 zdError(ZD_E_DB_SEEK, errno);
820 library->mapping = buffer;
822 /* Parse the header */
823 if(ZDParseHeader(library)) {
824 zdError(ZD_E_PARSE_HEADER, 0);
832 ZDCloseDatabase(library);
836 ZoneDetect *ZDOpenDatabase(const char *path)
838 ZoneDetect *const library = malloc(sizeof *library);
841 memset(library, 0, sizeof(*library));
843 #if defined(_MSC_VER) || defined(__MINGW32__)
844 library->fd = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
845 if (library->fd == INVALID_HANDLE_VALUE) {
846 zdError(ZD_E_DB_OPEN, (int)GetLastError());
850 const DWORD fsize = GetFileSize(library->fd, NULL);
851 if (fsize == INVALID_FILE_SIZE) {
852 zdError(ZD_E_DB_SEEK, (int)GetLastError());
855 library->length = (int32_t)fsize;
857 library->fdMap = CreateFileMappingA(library->fd, NULL, PAGE_READONLY, 0, 0, NULL);
858 if (!library->fdMap) {
859 zdError(ZD_E_DB_MMAP, (int)GetLastError());
863 library->mapping = MapViewOfFile(library->fdMap, FILE_MAP_READ, 0, 0, 0);
864 if (!library->mapping) {
865 zdError(ZD_E_DB_MMAP_MSVIEW, (int)GetLastError());
869 library->fd = open(path, O_RDONLY | O_CLOEXEC);
870 if(library->fd < 0) {
871 zdError(ZD_E_DB_OPEN, errno);
875 library->length = lseek(library->fd, 0, SEEK_END);
876 if(library->length <= 0 || library->length > 50331648) {
877 zdError(ZD_E_DB_SEEK, errno);
880 lseek(library->fd, 0, SEEK_SET);
882 library->mapping = mmap(NULL, (size_t)library->length, PROT_READ, MAP_PRIVATE | MAP_FILE, library->fd, 0);
883 if(library->mapping == MAP_FAILED) {
884 zdError(ZD_E_DB_MMAP, errno);
889 /* Parse the header */
890 if(ZDParseHeader(library)) {
891 zdError(ZD_E_PARSE_HEADER, 0);
899 ZDCloseDatabase(library);
903 ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone)
905 const int32_t latFixedPoint = ZDFloatToFixedPoint(lat, 90, library->precision);
906 const int32_t lonFixedPoint = ZDFloatToFixedPoint(lon, 180, library->precision);
907 size_t numResults = 0;
908 uint64_t distanceSqrMin = (uint64_t)-1;
910 /* Iterate over all polygons */
911 uint32_t bboxIndex = library->bboxOffset;
912 uint32_t metadataIndex = 0;
913 uint32_t polygonIndex = 0;
915 ZoneDetectResult *results = malloc(sizeof *results);
920 uint32_t polygonId = 0;
922 while(bboxIndex < library->metadataOffset) {
923 int32_t minLat, minLon, maxLat, maxLon, metadataIndexDelta;
924 uint64_t polygonIndexDelta;
925 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLat)) break;
926 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLon)) break;
927 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLat)) break;
928 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLon)) break;
929 if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break;
930 if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
932 metadataIndex += (uint32_t)metadataIndexDelta;
933 polygonIndex += (uint32_t)polygonIndexDelta;
935 if(latFixedPoint >= minLat) {
936 if(latFixedPoint <= maxLat &&
937 lonFixedPoint >= minLon &&
938 lonFixedPoint <= maxLon) {
940 const ZDLookupResult lookupResult = ZDPointInPolygon(library, library->dataOffset + polygonIndex, latFixedPoint, lonFixedPoint, (safezone) ? &distanceSqrMin : NULL);
941 if(lookupResult == ZD_LOOKUP_PARSE_ERROR) {
943 } else if(lookupResult != ZD_LOOKUP_NOT_IN_ZONE) {
944 ZoneDetectResult *const newResults = realloc(results, sizeof *newResults * (numResults + 2));
947 results = newResults;
948 results[numResults].polygonId = polygonId;
949 results[numResults].metaId = metadataIndex;
950 results[numResults].numFields = library->numFields;
951 results[numResults].fieldNames = library->fieldNames;
952 results[numResults].lookupResult = lookupResult;
961 /* The data is sorted along minLat */
968 /* Clean up results */
969 for(size_t i = 0; i < numResults; i++) {
971 ZDLookupResult overrideResult = ZD_LOOKUP_IGNORE;
972 for(size_t j = i; j < numResults; j++) {
973 if(results[i].metaId == results[j].metaId) {
974 ZDLookupResult tmpResult = results[j].lookupResult;
975 results[j].lookupResult = ZD_LOOKUP_IGNORE;
977 /* This is the same result. Is it an exclusion zone? */
978 if(tmpResult == ZD_LOOKUP_IN_ZONE) {
980 } else if(tmpResult == ZD_LOOKUP_IN_EXCLUDED_ZONE) {
983 /* If on the bodrder then the final result is on the border */
984 overrideResult = tmpResult;
990 if(overrideResult != ZD_LOOKUP_IGNORE) {
991 results[i].lookupResult = overrideResult;
994 results[i].lookupResult = ZD_LOOKUP_IN_ZONE;
999 /* Remove zones to ignore */
1000 size_t newNumResults = 0;
1001 for(size_t i = 0; i < numResults; i++) {
1002 if(results[i].lookupResult != ZD_LOOKUP_IGNORE) {
1003 results[newNumResults] = results[i];
1007 numResults = newNumResults;
1009 /* Lookup metadata */
1010 for(size_t i = 0; i < numResults; i++) {
1011 uint32_t tmpIndex = library->metadataOffset + results[i].metaId;
1012 results[i].data = malloc(library->numFields * sizeof *results[i].data);
1013 if(results[i].data) {
1014 for(size_t j = 0; j < library->numFields; j++) {
1015 results[i].data[j] = ZDParseString(library, &tmpIndex);
1020 /* Write end marker */
1021 results[numResults].lookupResult = ZD_LOOKUP_END;
1022 results[numResults].numFields = 0;
1023 results[numResults].fieldNames = NULL;
1024 results[numResults].data = NULL;
1027 *safezone = sqrtf((float)distanceSqrMin) * 90 / (float)(1 << (library->precision - 1));
1033 void ZDFreeResults(ZoneDetectResult *results)
1035 unsigned int index = 0;
1041 while(results[index].lookupResult != ZD_LOOKUP_END) {
1042 if(results[index].data) {
1043 for(size_t i = 0; i < (size_t)results[index].numFields; i++) {
1044 if(results[index].data[i]) {
1045 free(results[index].data[i]);
1048 free(results[index].data);
1055 const char *ZDGetNotice(const ZoneDetect *library)
1057 return library->notice;
1060 uint8_t ZDGetTableType(const ZoneDetect *library)
1062 return library->tableType;
1065 const char *ZDLookupResultToString(ZDLookupResult result)
1068 case ZD_LOOKUP_IGNORE:
1072 case ZD_LOOKUP_PARSE_ERROR:
1073 return "Parsing error";
1074 case ZD_LOOKUP_NOT_IN_ZONE:
1075 return "Not in zone";
1076 case ZD_LOOKUP_IN_ZONE:
1078 case ZD_LOOKUP_IN_EXCLUDED_ZONE:
1079 return "In excluded zone";
1080 case ZD_LOOKUP_ON_BORDER_VERTEX:
1081 return "Target point is border vertex";
1082 case ZD_LOOKUP_ON_BORDER_SEGMENT:
1083 return "Target point is on border";
1089 #define ZD_E_COULD_NOT(msg) "could not " msg
1091 const char *ZDGetErrorString(int errZD)
1093 switch ((enum ZDInternalError)errZD) {
1099 return ZD_E_COULD_NOT("open database file");
1101 return ZD_E_COULD_NOT("retrieve database file size");
1103 return ZD_E_COULD_NOT("map database file to system memory");
1104 #if defined(_MSC_VER) || defined(__MINGW32__)
1105 case ZD_E_DB_MMAP_MSVIEW :
1106 return ZD_E_COULD_NOT("open database file view");
1107 case ZD_E_DB_MAP_EXCEPTION:
1108 return "I/O exception occurred while accessing database file view";
1109 case ZD_E_DB_MUNMAP_MSVIEW:
1110 return ZD_E_COULD_NOT("close database file view");
1112 case ZD_E_DB_MUNMAP :
1113 return ZD_E_COULD_NOT("unmap database");
1114 case ZD_E_DB_CLOSE :
1115 return ZD_E_COULD_NOT("close database file");
1116 case ZD_E_PARSE_HEADER :
1117 return ZD_E_COULD_NOT("parse database header");
1121 #undef ZD_E_COULD_NOT
1123 int ZDSetErrorHandler(void (*handler)(int, int))
1125 zdErrorHandler = handler;
1129 char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon)
1131 ZoneDetectResult *result = ZDLookup(library, lat, lon, NULL);
1136 char* output = NULL;
1138 if(result[0].lookupResult == ZD_LOOKUP_END) {
1142 char* strings[2] = {NULL};
1144 for(unsigned int i = 0; i < result[0].numFields; i++) {
1145 if(result[0].fieldNames[i] && result[0].data[i]) {
1146 if(library->tableType == 'T') {
1147 if(!strcmp(result[0].fieldNames[i], "TimezoneIdPrefix")) {
1148 strings[0] = result[0].data[i];
1150 if(!strcmp(result[0].fieldNames[i], "TimezoneId")) {
1151 strings[1] = result[0].data[i];
1154 if(library->tableType == 'C') {
1155 if(!strcmp(result[0].fieldNames[i], "Name")) {
1156 strings[0] = result[0].data[i];
1163 for(unsigned int i=0; i<sizeof(strings)/sizeof(char*); i++) {
1165 size_t partLength = strlen(strings[i]);
1166 if(partLength > 512) {
1169 length += partLength;
1179 output = (char*)malloc(length);
1181 for(unsigned int i=0; i<sizeof(strings)/sizeof(char*); i++) {
1183 strcat(output + strlen(output), strings[i]);
1188 ZDFreeResults(result);