[OSM-dev] [PATCH] osm2pgsql tile expiry

Steve Hill steve at nexusuk.org
Sun Feb 8 10:23:45 GMT 2009


I've been working on code to generate a list of dirty tiles while 
importing the deltas.  The attached patch introduces 2 new commandline 
options: "-e <zoomlevel>" and "-o <dirty tile list output file>".  So, 
specifying "-e 17 -o /tmp/dirty_tiles" when importing a delta will cause 
osm2pgsql to generate a list of all zoom level 17 tiles which the delta 
has made dirty and store it in /tmp/dirty_tiles.

Proviso: for polygons, it currently takes a simplistic approach of drawing 
a bounding box around the whole polygon and marking every tile in the box 
as dirty.  If the bounding box is large (over 30x30Km) the polygon is 
treated as a line instead, so only the perimeter will be marked as dirty 
(this is so that huge polygons don't expire vast numbers of tiles and is 
based on the assumption that we probably aren't going to shade the area 
of massive polygons).

The dirty tile list is maintained in memory as a binary tree and dumped to 
disk at the end of the run.

I'm running this code on OpenPisteMap and it seems to be working ok.  Does 
anyone have any objection to me committing these changes to the 
OpenStreetMap subversion server?

  - Steve
    xmpp:steve at nexusuk.org   sip:steve at nexusuk.org   http://www.nexusuk.org/

      Servatis a periculum, servatis a maleficum - Whisper, Evanescence
-------------- next part --------------
Index: output.h
===================================================================
--- output.h	(revision 13558)
+++ output.h	(working copy)
@@ -10,6 +10,7 @@
 #ifndef OUTPUT_H
 #define OUTPUT_H
 
+#include "middle.h"
 #include "keyvals.h"
 
 struct output_options {
@@ -22,6 +23,8 @@
   int cache;       /* Memory usable for cache in MB */
   struct middle_t *mid;  /* Mid storage to use */
   const char *style;     /* style file to use */
+  int expire_tiles_zoom;	/* Zoom level for tile expiry list */
+  const char *expire_tiles_filename;	/* File name to output expired tiles list to */
 };
 
 struct output_t {
Index: build_geometry.cpp
===================================================================
--- build_geometry.cpp	(revision 13558)
+++ build_geometry.cpp	(working copy)
@@ -176,6 +176,85 @@
    areas.clear();
 }
 
+static int coords2nodes(CoordinateSequence * coords, struct osmNode ** nodes) {
+    size_t			num_coords;
+    size_t			i;
+    Coordinate		coord;
+
+    num_coords = coords->getSize();
+    *nodes = (struct osmNode *) malloc(num_coords * sizeof(struct osmNode));
+
+    for (i = 0; i < num_coords; i++) {
+        coord = coords->getAt(i);
+        (*nodes)[i].lon = coord.x;
+        (*nodes)[i].lat = coord.y;
+    }
+    return num_coords;
+}
+
+int parse_wkt(const char * wkt, struct osmNode *** xnodes, int ** xcount, int * polygon) {
+    GeometryFactory		gf;
+    WKTReader		reader(&gf);
+    std::string		wkt_string(wkt);
+    Geometry *		geometry;
+    const Geometry *	subgeometry;
+    GeometryCollection *	gc;
+    CoordinateSequence *	coords;
+    size_t			num_geometries;
+    size_t			i;
+	
+    *polygon = 0;
+    try {
+        geometry = reader.read(wkt_string);
+        switch (geometry->getGeometryTypeId()) {
+            // Single geometries
+            case geos::GEOS_POLYGON:
+                // Drop through
+            case geos::GEOS_LINEARRING:
+                *polygon = 1;
+                // Drop through
+            case geos::GEOS_POINT:
+                // Drop through
+            case geos::GEOS_LINESTRING:
+                *xnodes = (struct osmNode **) malloc(2 * sizeof(struct osmNode *));
+                *xcount = (int *) malloc(sizeof(int));
+                coords = geometry->getCoordinates();
+                (*xcount)[0] = coords2nodes(coords, &((*xnodes)[0]));
+                (*xnodes)[1] = NULL;
+                delete coords;
+                break;
+            // Geometry collections
+            case geos::GEOS_MULTIPOLYGON:
+                *polygon = 1;
+                // Drop through
+            case geos::GEOS_MULTIPOINT:
+                // Drop through
+            case geos::GEOS_MULTILINESTRING:
+                gc = (GeometryCollection *) geometry;
+                num_geometries = gc->getNumGeometries();
+                *xnodes = (struct osmNode **) malloc((num_geometries + 1) * sizeof(struct osmNode *));
+                *xcount = (int *) malloc(num_geometries * sizeof(int));
+                for (i = 0; i < num_geometries; i++) {
+                    subgeometry = gc->getGeometryN(i);
+                    coords = subgeometry->getCoordinates();
+                    (*xcount)[0] = coords2nodes(coords, &((*xnodes)[i]));
+                    delete coords;
+                }
+                (*xnodes)[i] = NULL;
+                break;
+            default:
+                std::cerr << std::endl << "unexpected object type while processing PostGIS data" << std::endl;
+                delete geometry;
+                return -1;
+        }
+        delete geometry;
+    } catch (...) {
+        std::cerr << std::endl << "excepton caught parsing PostGIS data" << std::endl;
+        return -1;
+    }
+    return 0;
+}
+
 size_t build_geometry(int osm_id, struct osmNode **xnodes, int *xcount, int make_polygon) {
     size_t wkt_size = 0;
     std::auto_ptr<std::vector<Geometry*> > lines(new std::vector<Geometry*>);
Index: osm2pgsql.c
===================================================================
--- osm2pgsql.c	(revision 13558)
+++ osm2pgsql.c	(working copy)
@@ -481,6 +481,8 @@
     fprintf(stderr, "   -W|--password\tForce password prompt.\n");
     fprintf(stderr, "   -H|--host\t\tDatabase server hostname or socket location.\n");
     fprintf(stderr, "   -P|--port\t\tDatabase server port.\n");
+    fprintf(stderr, "   -e|--expire-tiles zoom\tCreate a tile expiry list for a zoom level.\n");
+    fprintf(stderr, "   -o|--expire-output filename\tOutput filename for expired tiles list.\n");
     fprintf(stderr, "   -h|--help\t\tHelp information.\n");
     fprintf(stderr, "   -v|--verbose\t\tVerbose output.\n");
     fprintf(stderr, "\n");
@@ -571,6 +573,8 @@
     int sanitize=0;
     int pass_prompt=0;
     int projection = PROJ_SPHERE_MERC;
+    int expire_tiles_zoom = -1;
+    const char *expire_tiles_filename = "dirty_tiles";
     const char *db = "gis";
     const char *username=NULL;
     const char *host=NULL;
@@ -607,10 +611,12 @@
             {"port",     1, 0, 'P'},
             {"help",     0, 0, 'h'},
             {"style",    1, 0, 'S'},
+            {"expire-tiles", 1, 0, 'e'},
+            {"expire-output", 1, 0, 'o'},
             {0, 0, 0, 0}
         };
 
-        c = getopt_long (argc, argv, "ab:cd:hlmMp:suvU:WH:P:E:C:S:", long_options, &option_index);
+        c = getopt_long (argc, argv, "ab:cd:hlmMp:suvU:WH:P:E:C:S:e:o:", long_options, &option_index);
         if (c == -1)
             break;
 
@@ -633,6 +639,8 @@
             case 'H': host=optarg; break;
             case 'P': port=optarg; break;
             case 'S': style=optarg; break;
+            case 'e': expire_tiles_zoom=atoi(optarg); break;
+            case 'o': expire_tiles_filename=optarg; break;
 
             case 'h':
             case '?':
@@ -690,6 +698,8 @@
     options.mid = slim ? &mid_pgsql : &mid_ram;
     options.cache = cache;
     options.style = style;
+    options.expire_tiles_zoom = expire_tiles_zoom;
+    options.expire_tiles_filename = expire_tiles_filename;
     out = &out_pgsql;
 
     out->start(&options);
Index: middle.h
===================================================================
--- middle.h	(revision 13558)
+++ middle.h	(working copy)
@@ -7,6 +7,8 @@
 #ifndef MIDDLE_H
 #define MIDDLE_H
 
+#include "osmtypes.h"
+
 struct keyval;
 struct member;
 struct output_options;
Index: expire-tiles.c
===================================================================
--- expire-tiles.c	(revision 0)
+++ expire-tiles.c	(revision 0)
@@ -0,0 +1,352 @@
+/*
+ * Dirty tile list generation
+ *
+ * Steve Hill <steve at nexusuk.org>
+ */
+
+#include <libpq-fe.h>
+#include <math.h>
+#include <stdlib.h>
+#include "expire-tiles.h"
+#include "output.h"
+#include "pgsql.h"
+#include "build_geometry.h"
+
+#define EARTH_CIRCUMFERENCE		40075016.68
+#define HALF_EARTH_CIRCUMFERENCE	(EARTH_CIRCUMFERENCE / 2)
+#define TILE_EXPIRY_LEEWAY		0.5		// How many tiles worth of space to leave either side of a changed feature
+#define EXPIRE_TILES_MAX_BBOX		30000		// Maximum width or height of a bounding box (metres)
+
+struct tile_subtree {
+	struct tile_subtree *	less;
+	struct tile_subtree *	greater;
+	int			y;
+};
+
+struct tile_tree {
+	struct tile_tree *	less;
+	struct tile_tree *	greater;
+	int			x;
+	struct tile_subtree *	subtree;
+};
+
+static int				map_width;
+static double				tile_width;
+static const struct output_options *	Options;
+static struct tile_tree *		dirty = NULL;
+static int				outcount;
+
+static void add_to_subtree(struct tile_subtree ** tree, int y) {
+	while (*tree) {
+		if (y < (*tree)->y) tree = &((*tree)->less);
+		else if (y > (*tree)->y) tree = &((*tree)->greater);
+		else return;	// Already in the tree
+	}
+	*tree = calloc(1, sizeof(**tree));
+	(*tree)->y = y;
+}
+
+static void add_to_tree(struct tile_tree ** tree, int x, int y) {
+	while (*tree) {
+		if (x < (*tree)->x) tree = &((*tree)->less);
+		else if (x > (*tree)->x) tree = &((*tree)->greater);
+		else {
+			add_to_subtree(&((*tree)->subtree), y);
+			return;
+		}
+	}
+	*tree = calloc(1, sizeof(**tree));
+	(*tree)->x = x;
+	add_to_subtree(&((*tree)->subtree), y);
+}
+
+static void output_and_destroy_subtree(FILE * outfile, struct tile_subtree ** tree, int x) {
+	if (! *tree) return;
+	output_and_destroy_subtree(outfile, &((*tree)->less), x);
+	if (outfile) {
+		outcount++;
+		if ((outcount <= 1) || (! (outcount % 1000))) {
+			fprintf(stderr, "\rWriting dirty tile list (%iK)", outcount / 1000);
+			fflush(stderr);
+		}
+		fprintf(outfile, "%i/%i/%i\n", Options->expire_tiles_zoom, x, (*tree)->y);
+	}
+	output_and_destroy_subtree(outfile, &((*tree)->greater), x);
+	free(*tree);
+}
+
+static void output_and_destroy_tree(FILE * outfile, struct tile_tree ** tree) {
+	if (! *tree) return;
+	output_and_destroy_tree(outfile, &((*tree)->less));
+	output_and_destroy_subtree(outfile, &((*tree)->subtree), (*tree)->x);
+	output_and_destroy_tree(outfile, &((*tree)->greater));
+	free(*tree);
+}
+
+void expire_tiles_stop(void) {
+	FILE *	outfile;
+
+	outcount = 0;
+	outfile = fopen(Options->expire_tiles_filename, "a");
+	output_and_destroy_tree(outfile, &dirty);
+	if (outfile) fclose(outfile);
+	else fprintf(stderr, "Failed to open expired tiles file.  Tile expiry list will now be written!\n");
+}
+
+void expire_tiles_init(const struct output_options *options) {
+	Options = options;
+	map_width = pow(2,Options->expire_tiles_zoom);
+	tile_width = EARTH_CIRCUMFERENCE / map_width;
+}
+
+static double coords_to_tile_x(double lon) {
+	return map_width * (0.5 + (lon / EARTH_CIRCUMFERENCE));
+}
+
+static double coords_to_tile_y(double lat) {
+	return map_width * (0.5 - (lat / EARTH_CIRCUMFERENCE));
+}
+
+static void expire_tile(int x, int y) {
+	add_to_tree(&dirty, x, y);
+}
+
+static int normalise_tile_x_coord(int x) {
+	x %= map_width;
+	if (x < 0) x = (map_width - x) + 1;
+	return x;
+}
+
+/*
+ * Expire tiles that a line crosses
+ */
+static void expire_tiles_from_line(double lon_a, double lat_a, double lon_b, double lat_b) {
+	double	tile_x_a;
+	double	tile_y_a;
+	double	tile_x_b;
+	double	tile_y_b;
+	double	temp;
+	double	x1;
+	double	y1;
+	double	x2;
+	double	y2;
+	double	hyp_len;
+	double	x_len;
+	double	y_len;
+	double	x_step;
+	double	y_step;
+	double	step;
+	double	next_step;
+	int	x;
+	int	y;
+	int	norm_x;
+
+	tile_x_a = coords_to_tile_x(lon_a);
+	tile_y_a = coords_to_tile_y(lat_a);
+	tile_x_b = coords_to_tile_x(lon_b);
+	tile_y_b = coords_to_tile_y(lat_b);
+	if (tile_x_a > tile_x_b) {
+		// We always want the line to go from left to right - swap the ends if it doesn't
+		temp = tile_x_b;
+		tile_x_b = tile_x_a;
+		tile_x_a = temp;
+		temp = tile_y_b;
+		tile_y_b = tile_y_a;
+		tile_y_a = temp;
+	}
+
+	x_len = tile_x_b - tile_x_a;
+	if (x_len > map_width / 2) {
+		// If the line is wider than half the map, assume it
+		// crosses the international date line.
+		// These coordinates get normalised again later
+		tile_x_a += map_width;
+		temp = tile_x_b;
+		tile_x_b = tile_x_a;
+		tile_x_a = temp;
+		temp = tile_y_b;
+		tile_y_b = tile_y_a;
+		tile_y_a = temp;
+	}
+	y_len = tile_y_b - tile_y_a;
+	hyp_len = sqrt(pow(x_len, 2) + pow(y_len, 2));	// Pythagoras
+	x_step = x_len / hyp_len;
+	y_step = y_len / hyp_len;
+//	fprintf(stderr, "Expire from line (%f,%f),(%f,%f) [%f,%f],[%f,%f] %fx%f hyp_len = %f\n", lon_a, lat_a, lon_b, lat_b, tile_x_a, tile_y_a, tile_x_b, tile_y_b, x_len, y_len, hyp_len);
+	
+	for (step = 0; step <= hyp_len; step ++) {
+		// Interpolate points 1 tile width apart
+		next_step = step + 1;
+		if (next_step > hyp_len) next_step = hyp_len;
+		x1 = tile_x_a + ((double)step * x_step);
+		y1 = tile_y_a + ((double)step * y_step);
+		x2 = tile_x_a + ((double)next_step * x_step);
+		y2 = tile_y_a + ((double)next_step * y_step);
+		
+//		printf("Expire from subline (%f,%f),(%f,%f)\n", x1, y1, x2, y2);
+		// The line (x1,y1),(x2,y2) is up to 1 tile width long
+		// x1 will always be <= x2
+		// We could be smart and figure out the exact tiles intersected,
+		// but for simplicity, treat the coordinates as a bounding box
+		// and expire everything within that box.
+		if (y1 > y2) {
+			temp = y2;
+			y2 = y1;
+			y1 = temp;
+		}
+		for (x = x1 - TILE_EXPIRY_LEEWAY; x <= x2 + TILE_EXPIRY_LEEWAY; x ++) {
+			norm_x =  normalise_tile_x_coord(x);
+			for (y = y1 - TILE_EXPIRY_LEEWAY; y <= y2 + TILE_EXPIRY_LEEWAY; y ++) {
+				expire_tile(norm_x, y);
+			}
+		}
+	}
+}
+
+/*
+ * Expire tiles within a bounding box
+ */
+int expire_tiles_from_bbox(double min_lon, double min_lat, double max_lon, double max_lat) {
+	double		width;
+	double		height;
+	int		min_tile_x;
+	int		min_tile_y;
+	int		max_tile_x;
+	int		max_tile_y;
+	int		iterator_x;
+	int		iterator_y;
+	int		norm_x;
+	int		ret;
+
+	if (Options->expire_tiles_zoom < 0) return 0;
+	width = max_lon - min_lon;
+	height = max_lat - min_lat;
+	if (width > HALF_EARTH_CIRCUMFERENCE + 1) {
+		// Over half the planet's width within the bounding box - assume the
+		// box crosses the international date line and split it into two boxes
+		ret = expire_tiles_from_bbox(-HALF_EARTH_CIRCUMFERENCE, min_lat, min_lon, max_lat);
+		ret += expire_tiles_from_bbox(max_lon, min_lat, HALF_EARTH_CIRCUMFERENCE, max_lat);
+		return ret;
+	}
+
+	if (width > EXPIRE_TILES_MAX_BBOX) return -1;
+	if (height > EXPIRE_TILES_MAX_BBOX) return -1;
+
+//	printf("Expire from bbox (%f,%f)-(%f,%f) %fx%f\n", min_lon, min_lat, min_lon, min_lat, width, height);
+
+	// Convert the box's Mercator coordinates into tile coordinates
+	min_tile_x = coords_to_tile_x(min_lon) - TILE_EXPIRY_LEEWAY;
+	max_tile_y = coords_to_tile_x(min_lat) + TILE_EXPIRY_LEEWAY;
+	max_tile_x = coords_to_tile_x(min_lon) + TILE_EXPIRY_LEEWAY;
+	min_tile_y = coords_to_tile_x(min_lat) - TILE_EXPIRY_LEEWAY;
+	if (min_tile_x < 0) min_tile_x = 0;
+	if (min_tile_y < 0) min_tile_y = 0;
+	if (max_tile_x > map_width) max_tile_x = map_width;
+	if (max_tile_y > map_width) max_tile_y = map_width;
+//	printf("BBOX: (%f %f) - (%f %f) [%i %i] - [%i %i]\n", min_lon, min_lat, max_lon, max_lat, min_tile_x, min_tile_y, max_tile_x, max_tile_y);
+	for (iterator_x = min_tile_x; iterator_x <= max_tile_x; iterator_x ++) {
+		norm_x =  normalise_tile_x_coord(iterator_x);
+		for (iterator_y = min_tile_y; iterator_y <= max_tile_y; iterator_y ++) {
+			expire_tile(norm_x, iterator_y);
+		}
+	}
+	return 0;
+}
+
+void expire_tiles_from_nodes_line(struct osmNode * nodes, int count) {
+	int	i;
+	double	last_lat;
+	double	last_lon;
+
+	if (Options->expire_tiles_zoom < 0) return;
+//	fprintf(stderr, "Expire from nodes_line (%i)\n", count);
+	if (count < 1) return;
+	last_lat = nodes[0].lat;
+	last_lon = nodes[0].lon;
+	if (count < 2) {
+		expire_tiles_from_bbox(last_lon, last_lat, last_lon, last_lat);
+		return;
+	}
+	for (i = 1; i < count; i ++) {
+		expire_tiles_from_line(last_lon, last_lat, nodes[i].lon, nodes[i].lat);
+		last_lat = nodes[i].lat;
+		last_lon = nodes[i].lon;
+	}
+}
+
+/*
+ * Calculate a bounding box from a list of nodes and expire all tiles within it
+ */
+void expire_tiles_from_nodes_poly(struct osmNode * nodes, int count, int osm_id) {
+	int	i;
+	int	got_coords = 0;
+	double	min_lon = 0.0;
+	double	min_lat = 0.0;
+	double	max_lon = 0.0;
+	double	max_lat = 0.0;
+        
+	if (Options->expire_tiles_zoom < 0) return;
+//	printf("Expire from nodes_poly (%i)\n", count);
+	for (i = 0; i < count; i++) {
+		if ((! got_coords) || (nodes[i].lon < min_lon)) min_lon = nodes[i].lon;
+		if ((! got_coords) || (nodes[i].lat < min_lat)) min_lat = nodes[i].lat;
+		if ((! got_coords) || (nodes[i].lon > max_lon)) max_lon = nodes[i].lon;
+		if ((! got_coords) || (nodes[i].lat > max_lat)) max_lat = nodes[i].lat;
+		got_coords = 1;
+	}
+	if (got_coords) {
+		if (expire_tiles_from_bbox(min_lon, min_lat, max_lon, max_lat)) {
+			// Bounding box too big - just expire tiles on the line
+			fprintf(stderr, "\rLarge polygon (%.0f x %.0f metres, OSM ID %i) - only expiring perimeter\n", max_lon - min_lon, max_lat - min_lat, osm_id);
+			expire_tiles_from_nodes_line(nodes, count);
+		}
+	}
+}
+
+static void expire_tiles_from_xnodes_poly(struct osmNode ** xnodes, int * xcount, int osm_id) {
+	int	i;
+
+	if (Options->expire_tiles_zoom < 0) return;
+        for (i = 0; xnodes[i]; i++) expire_tiles_from_nodes_poly(xnodes[i], xcount[i], osm_id);
+}
+
+static void expire_tiles_from_xnodes_line(struct osmNode ** xnodes, int * xcount) {
+	int	i;
+
+        for (i = 0; xnodes[i]; i++) expire_tiles_from_nodes_line(xnodes[i], xcount[i]);
+}
+
+void expire_tiles_from_wkt(const char * wkt, int osm_id) {
+	struct osmNode **	xnodes;
+	int *			xcount;
+	int			polygon;
+	int			i;
+
+	if (! parse_wkt(wkt, &xnodes, &xcount, &polygon)) {
+		if (polygon) expire_tiles_from_xnodes_poly(xnodes, xcount, osm_id);
+		else expire_tiles_from_xnodes_line(xnodes, xcount);
+		for (i = 0; xnodes[i]; i++) free(xnodes[i]);
+		free(xnodes);
+		free(xcount);
+	}
+}
+
+void expire_tiles_from_db(PGconn * sql_conn, int osm_id) {
+	PGresult *	res;
+	char		tmp[16];
+	char const *	paramValues[1];
+	int		tuple;
+	char *		wkt;
+
+	if (Options->expire_tiles_zoom < 0) return;
+	snprintf(tmp, sizeof(tmp), "%d", osm_id);
+	paramValues[0] = tmp;
+	res = pgsql_execPrepared(sql_conn, "get_way", 1, paramValues, PGRES_TUPLES_OK);
+	for (tuple = 0; tuple < PQntuples(res); tuple++) {
+		wkt = PQgetvalue(res, tuple, 0);
+		expire_tiles_from_wkt(wkt, osm_id);
+	}
+	PQclear(res);
+}
+
+
Index: expire-tiles.h
===================================================================
--- expire-tiles.h	(revision 0)
+++ expire-tiles.h	(revision 0)
@@ -0,0 +1,14 @@
+#ifndef EXPIRE_TILES_H
+#define EXPIRE_TILES_H
+
+#include "output.h"
+
+void expire_tiles_init(const struct output_options *options);
+void expire_tiles_stop(void);
+int expire_tiles_from_bbox(double min_lon, double min_lat, double max_lon, double max_lat);
+void expire_tiles_from_nodes_line(struct osmNode * nodes, int count);
+void expire_tiles_from_nodes_poly(struct osmNode * nodes, int count, int osm_id);
+void expire_tiles_from_wkt(const char * wkt, int osm_id);
+void expire_tiles_from_db(PGconn * sql_conn, int osm_id);
+
+#endif
Index: build_geometry.h
===================================================================
--- build_geometry.h	(revision 13558)
+++ build_geometry.h	(working copy)
@@ -29,6 +29,8 @@
 
 #include "osmtypes.h"
 
+int parse_wkt(const char * wkt, struct osmNode *** xnodes, int ** xcount, int * polygon);
+
 char *get_wkt_simple(struct osmNode *, int count, int polygon, double *area, double *int_x, double *int_y);
 
 char* get_wkt(size_t index);
Index: output-pgsql.c
===================================================================
--- output-pgsql.c	(revision 13558)
+++ output-pgsql.c	(working copy)
@@ -26,6 +26,7 @@
 #include "build_geometry.h"
 #include "middle.h"
 #include "pgsql.h"
+#include "expire-tiles.h"
 
 #define SRID (project_getprojinfo()->srs)
 
@@ -431,6 +432,7 @@
     char sql[2048], *v;
     int i;
 
+    expire_tiles_from_bbox(node_lon, node_lat, node_lon, node_lat);
     sprintf(sql, "%d\t", id);
     copy_to_table(t_point, sql);
 
@@ -628,6 +630,7 @@
     if (wkt && strlen(wkt)) {
 	/* FIXME: there should be a better way to detect polygons */
 	if (!strncmp(wkt, "POLYGON", strlen("POLYGON"))) {
+            expire_tiles_from_nodes_poly(nodes, count, id);
 	    if (area > 0.0) {
 		char tmp[32];
 		snprintf(tmp, sizeof(tmp), "%f", area);
@@ -636,6 +639,7 @@
 	    write_wkts(id, tags, wkt, t_poly);
 	    add_parking_node(id, tags, interior_lat, interior_lon);
 	} else {
+            expire_tiles_from_nodes_line(nodes, count);
 	    write_wkts(id, tags, wkt, t_line);
 	    if (roads)
 		write_wkts(id, tags, wkt, t_roads);
@@ -835,6 +839,7 @@
         char *wkt = get_wkt(i);
 
         if (strlen(wkt)) {
+            expire_tiles_from_wkt(wkt, -id);
             /* FIXME: there should be a better way to detect polygons */
             if (!strncmp(wkt, "POLYGON", strlen("POLYGON"))) {
                 double area = get_area(i);
@@ -951,11 +956,14 @@
             if( Options->slim )
                 pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id);\n", tables[i].name, tables[i].name);
         }
+        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "PREPARE get_way (int4) AS SELECT AsText(way) FROM %s WHERE osm_id = $1;\n", tables[i].name);
 
         pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s FROM STDIN", tables[i].name);
         tables[i].copyMode = 1;
     }
 
+    expire_tiles_init(options);
+
     options->mid->start(options);
 
     return 0;
@@ -1049,6 +1057,8 @@
 
     pgsql_out_cleanup();
     free_style();
+
+    expire_tiles_stop();
 }
 
 static int pgsql_add_node(int id, double lat, double lon, struct keyval *tags)
@@ -1163,6 +1173,7 @@
         exit_nicely();
     }
     pgsql_pause_copy(&tables[t_point]);
+    expire_tiles_from_db(tables[t_point].sql_conn, osm_id);
     pgsql_exec(tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_point].name, osm_id );
     Options->mid->nodes_delete(osm_id);
     return 0;
@@ -1177,6 +1188,9 @@
     pgsql_pause_copy(&tables[t_roads]);
     pgsql_pause_copy(&tables[t_line]);
     pgsql_pause_copy(&tables[t_poly]);
+    expire_tiles_from_db(tables[t_roads].sql_conn, osm_id);
+    expire_tiles_from_db(tables[t_line].sql_conn, osm_id);
+    expire_tiles_from_db(tables[t_poly].sql_conn, osm_id);
     pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, osm_id );
     pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, osm_id );
     pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, osm_id );
@@ -1201,6 +1215,9 @@
     pgsql_pause_copy(&tables[t_roads]);
     pgsql_pause_copy(&tables[t_line]);
     pgsql_pause_copy(&tables[t_poly]);
+    expire_tiles_from_db(tables[t_roads].sql_conn, -osm_id);
+    expire_tiles_from_db(tables[t_line].sql_conn, -osm_id);
+    expire_tiles_from_db(tables[t_poly].sql_conn, -osm_id);
     pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, -osm_id );
     pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, -osm_id );
     pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, -osm_id );


More information about the dev mailing list