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);
 }