h3.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "eclrtl.hpp"
  15. #include "h3.hpp"
  16. #define H3_VERSION "h3 plugin 1.0.0"
  17. ECL_H3_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  18. {
  19. /* Warning: This function may be called without the plugin being loaded fully.
  20. * It should not make any library calls or assume that dependent modules
  21. * have been loaded or that it has been initialised.
  22. *
  23. * Specifically: "The system does not call DllMain for process and thread
  24. * initialization and termination. Also, the system does not load
  25. * additional executable modules that are referenced by the specified module."
  26. */
  27. if (pb->size != sizeof(ECLPluginDefinitionBlock))
  28. return false;
  29. pb->magicVersion = PLUGIN_VERSION;
  30. pb->version = H3_VERSION;
  31. pb->moduleName = "lib_h3";
  32. pb->ECL = NULL;
  33. pb->flags = PLUGIN_IMPLICIT_MODULE;
  34. pb->description = "ECL plugin library for uber h3\n";
  35. return true;
  36. }
  37. #include <h3api.h>
  38. static void toSetOf(bool &__isAllResult, size32_t &__lenResult, void *&__result, H3Index *buff, int maxBuffLen)
  39. {
  40. int finger = 0;
  41. for (int i = 0; i < maxBuffLen; ++i)
  42. {
  43. if (buff[i] == 0)
  44. continue;
  45. if (i > finger)
  46. buff[finger] = buff[i];
  47. ++finger;
  48. }
  49. __isAllResult = false;
  50. __lenResult = finger * sizeof(H3Index);
  51. __result = buff;
  52. }
  53. namespace h3
  54. {
  55. //--------------------------------------------------------------------------------
  56. // ECL SERVICE ENTRYPOINTS
  57. //--------------------------------------------------------------------------------
  58. // Indexing Functions ---
  59. ECL_H3_API unsigned __int64 ECL_H3_CALL index(ICodeContext *_ctx, double lat, double lon, uint32_t resolution)
  60. {
  61. GeoCoord location;
  62. location.lat = ::degsToRads(lat);
  63. location.lon = ::degsToRads(lon);
  64. return ::geoToH3(&location, resolution);
  65. }
  66. ECL_H3_API void ECL_H3_CALL center(ICodeContext *_ctx, size32_t &__lenResult, void *&__result, unsigned __int64 index)
  67. {
  68. GeoCoord location;
  69. ::h3ToGeo(index, &location);
  70. __lenResult = 2 * sizeof(double);
  71. __result = rtlMalloc(__lenResult);
  72. double *cur = static_cast<double *>(__result);
  73. *cur++ = ::radsToDegs(location.lat);
  74. *cur = ::radsToDegs(location.lon);
  75. }
  76. ECL_H3_API void ECL_H3_CALL boundary(ICodeContext *_ctx, size32_t &__lenResult, void *&__result, unsigned __int64 index)
  77. {
  78. GeoBoundary boundary;
  79. ::h3ToGeoBoundary(index, &boundary);
  80. __lenResult = boundary.numVerts * 2 * sizeof(double);
  81. __result = rtlMalloc(__lenResult);
  82. double *cur = static_cast<double *>(__result);
  83. for (int v = 0; v < boundary.numVerts; v++)
  84. {
  85. *cur++ = ::radsToDegs(boundary.verts[v].lat);
  86. *cur++ = ::radsToDegs(boundary.verts[v].lon);
  87. }
  88. }
  89. // Introspection ---
  90. ECL_H3_API uint32_t ECL_H3_CALL resolution(ICodeContext *_ctx, unsigned __int64 index)
  91. {
  92. return ::h3GetResolution(index);
  93. }
  94. ECL_H3_API uint32_t ECL_H3_CALL baseCell(ICodeContext *_ctx, unsigned __int64 index)
  95. {
  96. return ::h3GetBaseCell(index);
  97. }
  98. #define H3_INDEX_MINSTRLEN 17
  99. ECL_H3_API void ECL_H3_CALL toString(ICodeContext *_ctx, size32_t &lenVarStr, char *&varStr, unsigned __int64 index)
  100. {
  101. varStr = static_cast<char *>(rtlMalloc(H3_INDEX_MINSTRLEN));
  102. ::h3ToString(index, varStr, H3_INDEX_MINSTRLEN);
  103. lenVarStr = strlen(varStr);
  104. }
  105. ECL_H3_API unsigned __int64 ECL_H3_CALL fromString(ICodeContext *_ctx, const char *strIdx)
  106. {
  107. return ::stringToH3(strIdx);
  108. }
  109. ECL_H3_API bool ECL_H3_CALL isValid(ICodeContext *_ctx, unsigned __int64 index)
  110. {
  111. return ::h3IsValid(index) != 0;
  112. }
  113. // Traversal ---
  114. ECL_H3_API void ECL_H3_CALL kRing(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, unsigned __int64 index, int32_t k)
  115. {
  116. int maxBuff = ::maxKringSize(k);
  117. H3Index *buff = static_cast<H3Index *>(rtlCalloc(maxBuff, sizeof(H3Index)));
  118. ::kRing(index, k, buff);
  119. toSetOf(__isAllResult, __lenResult, __result, buff, maxBuff);
  120. }
  121. ECL_H3_API void ECL_H3_CALL hexRing(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, unsigned __int64 index, uint32_t k)
  122. {
  123. int maxBuff = ::maxKringSize(k);
  124. H3Index *buff = static_cast<H3Index *>(rtlCalloc(maxBuff, sizeof(H3Index)));
  125. ::hexRing(index, k, buff);
  126. toSetOf(__isAllResult, __lenResult, __result, buff, maxBuff);
  127. }
  128. ECL_H3_API int32_t ECL_H3_CALL distance(ICodeContext *_ctx, unsigned __int64 indexFrom, unsigned __int64 indexTo)
  129. {
  130. return ::h3Distance(indexFrom, indexTo);
  131. }
  132. // Hierarchy ---
  133. ECL_H3_API unsigned __int64 ECL_H3_CALL parent(ICodeContext *_ctx, unsigned __int64 index, uint32_t resolution)
  134. {
  135. return ::h3ToParent(index, resolution);
  136. }
  137. ECL_H3_API void ECL_H3_CALL children(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, unsigned __int64 index, uint32_t resolution)
  138. {
  139. int maxBuff = ::maxH3ToChildrenSize(index, resolution);
  140. H3Index *buff = static_cast<H3Index *>(rtlCalloc(maxBuff, sizeof(H3Index)));
  141. ::h3ToChildren(index, resolution, buff);
  142. toSetOf(__isAllResult, __lenResult, __result, buff, maxBuff);
  143. }
  144. ECL_H3_API void ECL_H3_CALL compact(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, bool isAllIndexSet, size32_t lenIndexSet, const void *indexSet)
  145. {
  146. const int len = lenIndexSet / sizeof(H3Index);
  147. H3Index *buff = static_cast<H3Index *>(rtlCalloc(len, sizeof(H3Index)));
  148. ::compact(static_cast<const H3Index *>(indexSet), buff, len);
  149. toSetOf(__isAllResult, __lenResult, __result, buff, len);
  150. }
  151. ECL_H3_API void ECL_H3_CALL uncompact(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, bool isAllIndexSet, size32_t lenIndexSet, const void *indexSet, uint32_t resolution)
  152. {
  153. const int len = lenIndexSet / sizeof(H3Index);
  154. int maxBuff = ::maxUncompactSize(static_cast<const H3Index *>(indexSet), len, resolution);
  155. H3Index *buff = static_cast<H3Index *>(rtlCalloc(maxBuff, sizeof(H3Index)));
  156. ::uncompact(static_cast<const H3Index *>(indexSet), len, buff, maxBuff, resolution);
  157. toSetOf(__isAllResult, __lenResult, __result, buff, maxBuff);
  158. }
  159. // Regions ---
  160. uint32_t initPolygon(GeoCoord *verts, size32_t numVerts, uint32_t resolution, GeoPolygon &polygon)
  161. {
  162. polygon.geofence.numVerts = numVerts;
  163. polygon.geofence.verts = verts;
  164. polygon.numHoles = 0;
  165. polygon.holes = NULL;
  166. return ::maxPolyfillSize(&polygon, resolution);
  167. }
  168. ECL_H3_API void ECL_H3_CALL polyfill(ICodeContext *_ctx, bool &__isAllResult, size32_t &__lenResult, void *&__result, size32_t countBoundary, const byte **boundary, uint32_t resolution)
  169. {
  170. // Check for special case when points exceed 180 degrees (longtitude)
  171. // - https://github.com/uber/h3/issues/210
  172. GeoCoord *poly = static_cast<GeoCoord *>(rtlCalloc(countBoundary, sizeof(GeoCoord)));
  173. GeoCoord *wPoly = static_cast<GeoCoord *>(rtlCalloc(countBoundary, sizeof(GeoCoord)));
  174. GeoCoord *ePoly = static_cast<GeoCoord *>(rtlCalloc(countBoundary, sizeof(GeoCoord)));
  175. double west = 0;
  176. double east = 0;
  177. for (int i = 0; i < countBoundary; ++i)
  178. {
  179. const GeoCoord *row = (GeoCoord *)boundary[i];
  180. double lat = ::degsToRads(row->lat);
  181. double lon = ::degsToRads(row->lon);
  182. poly[i].lat = lat;
  183. poly[i].lon = lon;
  184. wPoly[i].lat = lat;
  185. wPoly[i].lon = lon > 0 ? 0 : lon;
  186. ePoly[i].lat = lat;
  187. ePoly[i].lon = lon <= 0 ? 0 : lon;
  188. if (i == 0)
  189. west = east = row->lon;
  190. else if (west > row->lon)
  191. west = row->lon;
  192. else if (east < row->lon)
  193. east = row->lon;
  194. }
  195. if (east - west >= 180)
  196. {
  197. GeoPolygon wPolygon, ePolygon;
  198. int wMaxBuff = initPolygon(wPoly, countBoundary, resolution, wPolygon);
  199. int eMaxBuff = initPolygon(ePoly, countBoundary, resolution, ePolygon);
  200. H3Index *buff = static_cast<H3Index *>(rtlCalloc(wMaxBuff + eMaxBuff, sizeof(H3Index)));
  201. ::polyfill(&wPolygon, resolution, buff);
  202. ::polyfill(&ePolygon, resolution, buff + wMaxBuff);
  203. toSetOf(__isAllResult, __lenResult, __result, buff, wMaxBuff + eMaxBuff);
  204. }
  205. else
  206. {
  207. GeoPolygon polygon;
  208. int maxBuff = initPolygon(poly, countBoundary, resolution, polygon);
  209. H3Index *buff = static_cast<H3Index *>(rtlCalloc(maxBuff, sizeof(H3Index)));
  210. ::polyfill(&polygon, resolution, buff);
  211. toSetOf(__isAllResult, __lenResult, __result, buff, maxBuff);
  212. }
  213. rtlFree(ePoly);
  214. rtlFree(wPoly);
  215. rtlFree(poly);
  216. }
  217. // Misc ---
  218. ECL_H3_API double ECL_H3_CALL degsToRads(ICodeContext *_ctx, double degrees)
  219. {
  220. return ::degsToRads(degrees);
  221. }
  222. ECL_H3_API double ECL_H3_CALL radsToDegs(ICodeContext *_ctx, double rads)
  223. {
  224. return ::radsToDegs(rads);
  225. }
  226. ECL_H3_API double ECL_H3_CALL hexAreaKm2(ICodeContext *_ctx, uint32_t resolution)
  227. {
  228. return ::hexAreaKm2(resolution);
  229. }
  230. ECL_H3_API double ECL_H3_CALL hexAreaM2(ICodeContext *_ctx, uint32_t resolution)
  231. {
  232. return ::hexAreaM2(resolution);
  233. }
  234. ECL_H3_API unsigned __int64 ECL_H3_CALL numHexagons(ICodeContext *_ctx, uint32_t resolution)
  235. {
  236. return ::numHexagons(resolution);
  237. }
  238. // ECL Optimized ---
  239. const unsigned __int64 MASK_CELL_BASE = 0b01111111;
  240. const unsigned __int64 MASK_CELL_RES = 0b00000111;
  241. const unsigned __int64 MASK_RES = 0b00001111;
  242. #define MASK_GET(valToMask, mask, shift) ((valToMask) >> (shift)) & (mask)
  243. #define MASK_SET(valToMask, mask, shift, val) (((valToMask) & ~(static_cast<unsigned __int64>(mask) << (shift))) | (static_cast<unsigned __int64>(val) << (shift)))
  244. ECL_H3_API void ECL_H3_CALL toECLIndex(ICodeContext *_ctx, char * __result, unsigned __int64 index)
  245. {
  246. for (unsigned int i = 0; i < 16; ++i) {
  247. const unsigned char resVal = MASK_GET(index, i == 0 ? MASK_CELL_BASE : MASK_CELL_RES, (15 - i) * 3);
  248. __result[i] = ((i == 0 && resVal == MASK_CELL_BASE) || (i > 0 && resVal == MASK_CELL_RES)) ? ' ' : '0' + resVal;
  249. }
  250. }
  251. ECL_H3_API void ECL_H3_CALL ECLIndex(ICodeContext *_ctx, char * __result, double lat, double lon, uint32_t resolution)
  252. {
  253. return toECLIndex(_ctx, __result, index(_ctx, lat, lon, resolution));
  254. }
  255. ECL_H3_API unsigned __int64 ECL_H3_CALL fromECLIndex(ICodeContext *_ctx, const char *eclIndex)
  256. {
  257. unsigned __int64 __result = 646078419604526808; // lat:0, lng:0, res:15
  258. unsigned __int64 res = 0;
  259. for (unsigned int i = 0; i < 16; ++i) {
  260. unsigned char resVal = eclIndex[i];
  261. if (resVal == ' ') {
  262. resVal = i == 0 ? MASK_CELL_BASE : MASK_CELL_RES;
  263. } else {
  264. resVal -= '0';
  265. res = i;
  266. }
  267. __result = MASK_SET(__result, i == 0 ? MASK_CELL_BASE : MASK_CELL_RES, (15 - i) * 3, resVal);
  268. }
  269. return MASK_SET(__result, MASK_RES, 52, res);
  270. }
  271. ECL_H3_API uint32_t ECL_H3_CALL ECLIndexResolution(ICodeContext *_ctx, const char *eclIndex)
  272. {
  273. // This is equivilant to: LENGTH(TRIM(eclIndex)) - 1;
  274. for (unsigned int i = 1; i < 16; ++i) {
  275. if (eclIndex[i] == ' ') {
  276. return i - 1;
  277. }
  278. }
  279. return 15;
  280. }
  281. ECL_H3_API void ECL_H3_CALL ECLIndexParent(ICodeContext *_ctx, char * __result, const char *eclIndex, uint32_t resolution)
  282. {
  283. // This is equivilant to: eclIndex[1..resolution + 1];
  284. for (unsigned int i = 0; i < 16; ++i) {
  285. __result[i] = i <= resolution ? eclIndex[i] : ' ';
  286. }
  287. }
  288. } // namespace h3