Mercurial > hg > batmud > maputils
changeset 2101:a2c893c39771
Implement 'list locations near coordinates' functionality in mapsearch backend.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 10 Sep 2019 16:29:37 +0300 |
parents | 9141edfb5647 |
children | 861137feb73c |
files | mapsearch.c |
diffstat | 1 files changed, 173 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/mapsearch.c Tue Sep 10 16:23:55 2019 +0300 +++ b/mapsearch.c Tue Sep 10 16:29:37 2019 +0300 @@ -12,6 +12,7 @@ #include <sys/types.h> #include <pwd.h> #include <grp.h> +#include <math.h> /* Default settings etc. constants @@ -79,6 +80,9 @@ } MAPMatch; +LocMarker **optMapLocations = NULL; +int optNMapLocations = 0; + /* Options */ MAPInfoCtx optMaps[SET_MAX_MAPS]; @@ -1170,6 +1174,53 @@ } +int mapCompareDistance(const void *pa, const void *pb) +{ + const LocMarker *va = (const LocMarker *) pa, + *vb = (const LocMarker *) pb; + + return va->val - vb->val; +} + + +int mapNearbyLocationSearch(LocMarker ***pnearest, + const int xc, const int yc, const int maxDist, char **verr) +{ + int nnearest = 0; + LocMarker **nearest; + + // Allocate memory for results + if ((*pnearest = nearest = th_malloc(sizeof(LocMarker *) * optNMapLocations)) == NULL) + { + *verr = "Could not allocate memory for temporary sorting buffer."; + goto out; + } + + // Calculate distances and filter by maxDist + for (int nloc = 0; nloc < optNMapLocations; nloc++) + { + LocMarker *marker = optMapLocations[nloc]; + int dx = xc - (marker->xc + marker->file->xoffs), + dy = yc - (marker->yc + marker->file->yoffs), + dist = sqrt(dx * dx + dy * dy); + + if (maxDist < 0 || dist <= maxDist) + { + marker->val = dist; + nearest[nnearest++] = marker; + } + } + + // Sort the locations based on distance + if (nnearest > 0) + qsort(nearest, sizeof(LocMarker *), nnearest, mapCompareDistance); + +out: + + return nnearest; +} + + void mapLocationSearch(struct lws *wsi, const unsigned char *data, const size_t len, char **verr) { MAPMatch matches[SET_MAX_MATCHES]; @@ -1256,6 +1307,89 @@ } +void mapNearLocationSearch(struct lws *wsi, const unsigned char *data, const size_t len, char **verr) +{ + MAPMatch matches[SET_MAX_MATCHES]; + LocMarker **nearest = NULL; + int findXC, findYC, maxDist, nnearest = 0; + size_t offs = 0; + + // Get coordinates + if (!mapParseIntValue(data, len, &offs, &findXC)) + { + *verr = "Invalid search query, no global X coordinate specified."; + goto out; + } + + offs++; + + if (!mapParseIntValue(data, len, &offs, &findYC)) + { + *verr = "Invalid search query, no global Y coordinate specified."; + goto out; + } + + offs++; + + // Get max distance, default to some value + if (!mapParseIntValue(data, len, &offs, &maxDist)) + maxDist = 200; + + // Check values + if (findXC < 0 || findYC < 0) + { + *verr = "Invalid search coordinates."; + goto out; + } + + if (maxDist < 1 || maxDist > 1000) + { + *verr = "Invalid maximum search distance."; + goto out; + } + + // Find nearest locations + nnearest = mapNearbyLocationSearch(&nearest, findXC, findYC, maxDist, verr); + if (*verr != NULL) + goto out; + + if (nnearest >= SET_MAX_MATCHES) + nnearest = SET_MAX_MATCHES; + + for (int nloc = 0; nloc < nnearest; nloc++) + { + LocMarker *marker = nearest[nloc]; + MAPMatch *match = &matches[nloc]; + + match->marker = marker; + match->nname = 0; + match->mx = marker->xc + 1; + match->my = marker->yc + 1; + match->wx = optWorldXC + marker->file->xoffs + marker->xc; + match->wy = optWorldYC + marker->file->yoffs + marker->yc; + } + +out: + th_free(nearest); + + // If an error occured, bail out now + if (*verr != NULL) + return; + + // We got some matches, output them as a JSON array + char *buf; + size_t bufLen; + + mapCreateResultStr(&buf, &bufLen, + matches, nnearest, SET_MAX_MATCHES, + FALSE, FALSE, 0, 0, NULL); + + mapMSG(2, "%s\n", buf); + mapLWSWrite(wsi, (unsigned char *) buf, bufLen); + th_free(buf); +} + + int mapLWSCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) @@ -1319,6 +1453,11 @@ mapLocationSearch(wsi, udata + 10, len - 10, &verr); } else + if (len >= 8 + 3 && strncmp(data, "LOCNEAR:", 8) == 0) + { + mapNearLocationSearch(wsi, udata + 8, len - 8, &verr); + } + else { // Unknown or invalid query verr = "Invalid command, and/or not enough data."; @@ -1458,6 +1597,38 @@ fclose(fh); } + // Get total number of non-invis locations + optNMapLocations = 0; + for (int nmap = 0; nmap < optNMaps; nmap++) + { + MAPInfoCtx *info = &optMaps[nmap]; + for (int nloc = 0; nloc < info->loc.nlocations; nloc++) + { + LocMarker *marker = info->loc.locations[nloc]; + if ((marker->flags & LOCF_INVIS) == 0) + optNMapLocations++; + } + } + + // Create large array of location pointers for sorting purposes + if ((optMapLocations = th_malloc(sizeof(LocMarker *) * optNMapLocations)) == NULL) + { + mapERR("Could not allocate memory for location sorting buffer of %d entries.\n", + optNMapLocations); + return FALSE; + } + + for (int nmap = 0, index = 0; nmap < optNMaps; nmap++) + { + MAPInfoCtx *info = &optMaps[nmap]; + for (int nloc = 0; nloc < info->loc.nlocations; nloc++) + { + LocMarker *marker = info->loc.locations[nloc]; + if ((marker->flags & LOCF_INVIS) == 0) + optMapLocations[index++] = marker; + } + } + return TRUE; } @@ -1471,6 +1642,8 @@ mapBlockFree(info->map); locFreeMapLocations(&info->loc); } + + th_free_r(&optMapLocations); }