aboutsummaryrefslogtreecommitdiffstats
path: root/tileserver/tileset.c
diff options
context:
space:
mode:
Diffstat (limited to 'tileserver/tileset.c')
-rw-r--r--tileserver/tileset.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/tileserver/tileset.c b/tileserver/tileset.c
new file mode 100644
index 000000000..b4e91d37d
--- /dev/null
+++ b/tileserver/tileset.c
@@ -0,0 +1,203 @@
+/*
+ * tileset.c:
+ * Interface to an individual tile set.
+ *
+ * Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
+ * Email: chris@mysociety.org; WWW: http://www.mysociety.org/
+ *
+ */
+
+static const char rcsid[] = "$Id: tileset.c,v 1.1 2006-09-20 10:25:14 chris Exp $";
+
+/*
+ * Tile sets are stored in directory trees which contain indices of tile
+ * locations to tile IDs, packed archives of tile images, and indices of where
+ * each tile image lives in the corresponding packed file. Tile IDs are SHA1
+ * digests of the tile contents, enabling efficient storage in the presence of
+ * repeated tiles.
+ *
+ * Note that this doesn't have attractive properties for locality of reference.
+ * That will need fixing if performance under the current scheme is not
+ * acceptable.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "cdb.h"
+#include "tileset.h"
+
+#define TILEID_LEN 20
+
+struct tileset {
+ char *t_path, *t_pathbuf;
+ cdb t_tileid_idx;
+ /* Tile IDs are stored in the tile ID database in N-by-N square blocks,
+ * so that the tile ID for (X, Y) is obtained by getting the block for
+ * (X / N, Y / N) and looking up (X, Y) within it. The blocking factor is
+ * (hopefully!) chosen to be about a disk block in size. It is stored in
+ * the tile ID database. Rather than allocating a specific tile ID for a
+ * tile which is not present, each information block is preceded by a
+ * coverage bitmap. */
+ unsigned t_blocking;
+ bool t_first;
+ unsigned t_x, t_y;
+ cdb_datum t_block;
+ /* File pointer open on an image file and the name of the file it's open
+ * on. */
+ char *t_imgfile;
+ FILE *t_fpimg;
+};
+
+
+/* tileset_open PATH
+ * Open the tileset at PATH, returning a tileset object on success or NULL on
+ * failure. */
+tileset tileset_open(const char *path) {
+ struct tileset *T, Tz = {0};
+ static char *path;
+ static size_t pathlen;
+ size_t l;
+ cdb_datum d = NULL;
+ char *s;
+ int i;
+
+ T = xmalloc(sizeof *T);
+ *T = Tz;
+
+ T->t_first = 1;
+ T->t_path = xstrdup(path);
+ T->t_pathbuf = xmalloc(strlen(path) + sizeof "/a/b/c/index.cdb");
+
+ /* Open the tile ID index. */
+ sprintf(T->t_pathbuf, "%s/index.cdb", T->t_path);
+ if (!(T->t_tileid_idx = cdb_open(T->t_pathbuf)))
+ goto fail;
+
+ /* get blocking factor */
+ if (!(d = cdb_get_string(T->t_tileid_idx, "blocking")))
+ goto fail;
+ s = (char*)d->cd_buf;
+ if (!(i = atoi(s)) || i < 0)
+ goto fail;
+ T->t_blocking = (unsigned)i;
+
+ cdb_datum_free(d);
+
+ return T;
+
+fail:
+ tileset_free(T);
+ if (d) cdb_datum_free(d);
+ return NULL;
+}
+
+/* tileset_close T
+ * Free resources associated with T. */
+void tileset_close(tileset T) {
+ if (!T) return;
+ cdb_close(T->t_tileid_idx);
+ xfree(T->t_path);
+ xfree(T->t_pathbuf);
+ xfree(T);
+}
+
+static size_t blockmap_bitmap_len(const unsigned blocking) {
+ return (blocking * blocking + 8) / 8;
+}
+
+static size_t blockmap_len(const unsigned blocking) {
+ size_t l;
+ /* Bitmap of null tiles. */
+ l = blockmap_bitmap_len(blocking);
+ /* Tile IDs themselves */
+ l += blocking * blocking * TILEID_LEN;
+ return l;
+}
+
+/* tileset_get_tileid T X Y ID
+ * Write into ID the tile ID of the tile at (X, Y) in T, returning true on
+ * success or false on failure. */
+bool tileset_get_tileid(tileset T, const unsigned x, const unsigned y,
+ uint8_t *id) {
+ unsigned x2, y2, off, off0;
+ uint8_t *b;
+
+ if (T->t_first || T->t_x != x || T->t_y != y) {
+ /* Grab block from database. */
+ char buf[32];
+
+ T->t_first = 0;
+ if (T->t_block) cdb_datum_free(T->t_block);
+
+ sprintf(buf, "%u,%u", x / T->t_blocking, y / T->t_blocking);
+ T->t_block = cdb_get_string(T->t_tileid_idx, buf);
+ if (!T->block)
+ return 0;
+ }
+
+ if (T->t_block->cd_len != blockmap_len(T->t_blocking))
+ return 0;
+ /* XXX also report bogus ID block */
+
+ b = (uint8_t*)T->t_block->cd_buf;
+
+ x2 = x % T->t_blocking;
+ y2 = y % T->t_blocking;
+ off = (x2 + y2 * T->t_blocking);
+
+ /* For a tile not present the corresponding bit in the bitmap is set. */
+ if (b[off >> 3] & (1 << (off & 7)))
+ return 0;
+
+ off0 = blockmap_bitmap_len(T->t_blocking);
+ memcpy(id, b + off0 + off * TILEID_LEN, TILEID_LEN);
+
+ return 1;
+}
+
+/* tileset_get_tile T ID LEN
+ * Retrieve the tile identified by ID, writing its length into *LEN and
+ * returning a malloced buffer containing its contents on success, or returning
+ * NULL on failure. */
+void *tileset_get_tile(tileset T, const uint8_t *id, size_t *len) {
+ cdb idx = NULL;
+ cdb_datum d = NULL;
+ void *ret = NULL;
+ unsigned off, len;
+ FILE *fp = NULL;
+
+ sprintf(T->t_pathbuf, "%s/%x/%x/%x/index.cdb",
+ T->t_path, (unsigned)(id[0] >> 4),
+ (unsigned)(id[0] & 0xf), (unsigned)(id[1] >> 4));
+ if (!(idx = cdb_open(T->t_pathbuf)))
+ return NULL;
+ /* also maybe report bogus index */
+
+ if (!(d = cdb_get_string(idx, T->t_pathbuf)))
+ goto fail;
+
+ if (2 != sscanf((char*)d->cd_buf, "%x:%x", &off, &len))
+ goto fail;
+
+ sprintf(T->t_pathbuf, "%s/%x/%x/%x/tiles",
+ T->t_path, (unsigned)(id[0] >> 4),
+ (unsigned)(id[0] & 0xf), (unsigned)(id[1] >> 4));
+ if (!(fp = fopen(T->t_pathbuf, "rb")))
+ goto fail;
+ else if (-1 == fseek(fp, off, SEEK_SET))
+ goto fail;
+
+ ret = xmalloc(len);
+ if (len != fread(ret, 1, len, fp)) {
+ xfree(ret);
+ goto fail;
+ }
+
+fail:
+ if (idx) cdb_close(idx);
+ if (d) cdb_datum_free(d);
+ if (fp) fclose(fp);
+ return ret;
+}