Index: reader/osm/SeaGenerator.java
===================================================================
--- reader/osm/SeaGenerator.java	(revision 2443)
+++ reader/osm/SeaGenerator.java	(working copy)
@@ -34,6 +34,7 @@
 import java.util.zip.GZIPInputStream;
 
 import uk.me.parabola.imgfmt.MapFailedException;
+import uk.me.parabola.imgfmt.Utils;
 import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.log.Logger;
@@ -52,6 +53,10 @@
  */
 public class SeaGenerator extends OsmReadingHooksAdaptor {
 	private static final Logger log = Logger.getLogger(SeaGenerator.class);
+	
+	static final byte SEA_TILE = 's';
+	static final byte LAND_TILE = 'l';
+	static final byte MIXED_TILE = 'm';
 
 	private boolean generateSeaUsingMP = true;
 	private int maxCoastlineGap;
@@ -81,7 +86,17 @@
 	 * precompiled sea should not be used.
 	 */
 	private File precompSeaDir;
-	private static ThreadLocal<Map<String,String>> precompIndex = new ThreadLocal<Map<String,String>>();
+	// the index is a grid. The element values are SEA_TYPE, LAND_TYPE, or MIXED_TYPE, or 0 for unknown
+	private static ThreadLocal<byte[][]> precompIndex = new ThreadLocal<byte[][]>();
+	private static ThreadLocal<String> precompSeaExt = new ThreadLocal<String>();
+	private static ThreadLocal<String> precompSeaPrefix = new ThreadLocal<String>();
+	// the index covers planet
+	private final int indexMinLat = Utils.toMapUnit(-90.0);
+	private final int indexMaxLat = Utils.toMapUnit(90.0);
+	private final int indexMinLon = Utils.toMapUnit(-180.0);
+	private final int indexMaxLon = Utils.toMapUnit(180.0);
+	private final static Pattern keySplitter = Pattern.compile(Pattern.quote("_"));
+	
 
 	private static final List<Class<? extends LoadableMapDataSource>> precompSeaLoader;
 
@@ -129,6 +144,9 @@
 					}
 					
 					if (indexFile.exists()) {
+						int indexWidth = (getTileStart(indexMaxLon) - getTileStart(indexMinLon)) / PRECOMP_RASTER;
+						int indexHeight = (getTileStart(indexMaxLat) - getTileStart(indexMinLat)) / PRECOMP_RASTER;
+						
 						try {
 							InputStream fileStream = new FileInputStream(indexFile);
 							if (indexFile.getName().endsWith(".gz")) {
@@ -139,23 +157,56 @@
 							Pattern csvSplitter = Pattern.compile(Pattern
 									.quote(";"));
 							String indexLine = null;
-							Map<String, String> indexItems = new HashMap<String, String>();
+							
+							byte[][] indexGrid = new byte[indexWidth+1][indexHeight+1];
+							boolean detectExt = true; 
+							String prefix = null;
+							String ext = null;
+							
 							while ((indexLine = indexReader.readLine()) != null) {
+								if (indexLine.startsWith("#")) {
+									// comment
+									continue;
+								}
 								String[] items = csvSplitter.split(indexLine);
 								if (items.length != 2) {
 									log.warn("Invalid format in index file: "
 											+ indexLine);
 									continue;
 								}
-								if (items[0].startsWith("#")) {
-									// comment
+								String precompKey = items[0];
+								byte type = updateIndex(precompKey, items[1], indexGrid);
+								if (type == '?'){
+									log.warn("Invalid format in index file: "
+											+ indexLine);
 									continue;
 								}
-								indexItems.put(items[0].trim().intern(),
-										items[1].trim().intern());
+								if (type == MIXED_TILE){
+									// make sure that all file names are using the same name scheme
+									int prePos = items[1].indexOf(items[0]);
+									if (prePos >= 0){
+										if (detectExt){
+											prefix = items[1].substring(0, prePos);
+											ext = items[1].substring(prePos+items[0].length());
+											detectExt = false;
+										} else {
+											StringBuilder sb = new StringBuilder(prefix);
+											sb.append(precompKey);
+											sb.append(ext);												
+											if (items[1].equals(sb.toString()) == false){
+												log.warn("Unexpected file name in index file: "
+														+ indexLine);
+											}
+										}
+									}
+								}
+
 							}
 							indexReader.close();
-							precompIndex.set(indexItems);
+							precompIndex.set(indexGrid);
+							precompSeaPrefix.set(prefix);
+							precompSeaExt.set(ext);
+							
 						} catch (IOException exp) {
 							log.error("Cannot read index file " + indexFile,
 									exp);
@@ -260,6 +311,22 @@
 		return generateSea;
 	}
 	
+	/**
+	 * Retrieves the start value of the precompiled tile.
+	 * @param value the value for which the start value is calculated
+	 * @return the tile start value
+	 */
+	public static int getTileStart(int value) {
+		int rem = value % PRECOMP_RASTER;
+		if (rem == 0) {
+			return value;
+		} else if (value >= 0) {
+			return value - rem;
+		} else {
+			return value - PRECOMP_RASTER - rem;
+		}
+	}
+	
 	public Set<String> getUsedTags() {
 		HashSet<String> usedTags = new HashSet<String>();
 		if (coastlineFilenames == null) {
@@ -394,6 +461,56 @@
 	}
 	
 	/**
+	 * Get the tile name from the index. 
+	 * @param precompKey The key name is compiled of {@code lat+"_"+lon}. 
+	 * @return either "land" or "sea" or a file name or null
+	 */
+	private String getTileName(String precompKey){
+		byte[][] index = precompIndex.get();
+		String[] tileCoords = keySplitter.split(precompKey);
+		int lat = Integer.valueOf(tileCoords[0]); 
+		int lon = Integer.valueOf(tileCoords[1]); 
+		int latIndex = (indexMaxLat-lat) / PRECOMP_RASTER;
+		int lonIndex = (indexMaxLon-lon) / PRECOMP_RASTER;
+		byte type = index[lonIndex][latIndex]; 
+		switch (type){
+		case SEA_TILE: return "sea"; 
+		case LAND_TILE: return "land"; 
+		case MIXED_TILE: return precompSeaPrefix.get() + precompKey + precompSeaExt.get(); 
+		default:  return null;
+		}
+	}
+	
+
+	/**
+	 * Update the index grid for the element identified by precompKey. 
+	 * @param precompKey The key name is compiled of {@code lat+"_"+lon}. 
+	 * @param fileName either "land", "sea", or a file name containing OSM data
+	 * @param indexGrid the previously allocated index grid  
+	 * @return the byte that was saved in the index grid 
+	 */
+	private byte updateIndex (String precompKey, String fileName, byte[][] indexGrid){
+		String[] tileCoords = keySplitter.split(precompKey);
+		byte type = '?';
+		if (tileCoords.length == 2){
+			int lat = Integer.valueOf(tileCoords[0]); 
+			int lon = Integer.valueOf(tileCoords[1]); 
+			int latIndex = (indexMaxLat - lat) / PRECOMP_RASTER;
+			int lonIndex = (indexMaxLon - lon) / PRECOMP_RASTER;
+
+			if ("sea".equals(fileName))
+				type = SEA_TILE;
+			else if ("land".equals(fileName))
+				type = LAND_TILE;
+			else 
+				type = MIXED_TILE;
+
+			indexGrid[lonIndex][latIndex] = type;
+		}
+		return type;
+	}
+	
+	/**
 	 * Loads the precompiled sea tiles and adds the data to the 
 	 * element saver.
 	 */
@@ -406,14 +523,14 @@
 		
 		List<Way> landWays = new ArrayList<Way>();
 		List<Way> seaWays = new ArrayList<Way>();
+		// get the index with assignment key => sea/land/tilename
 		
-		// get the index with assignment key => sea/land/tilename
-		Map<String,String> index = precompIndex.get();
+		
 		for (String precompKey : getPrecompKeyNames()) {
-			String tileName = index.get(precompKey);
+			String tileName = getTileName(precompKey);
 			
-			if (tileName == null) {
-				log.error("Precompile sea tile "+tileName+" is missing in the index. Skipping.");
+			if (tileName == null ) {
+				log.error("Precompile sea tile "+precompKey+" is missing in the index. Skipping.");
 				continue;
 			}
 			
@@ -422,8 +539,7 @@
 				// => create a rectangle that covers the whole precompiled tile 
 				Way w = new Way(FakeIdGenerator.makeFakeId());
 				w.addTag("natural", tileName);
-				
-				String[] tileCoords = precompKey.split(Pattern.quote("_"));
+				String[] tileCoords = keySplitter.split(precompKey);
 				int minLat = Integer.valueOf(tileCoords[0]);
 				int minLon = Integer.valueOf(tileCoords[1]);
 				int maxLat = minLat + PRECOMP_RASTER;
Index: sea/optional/PrecompSeaGenerator.java
===================================================================
--- sea/optional/PrecompSeaGenerator.java	(revision 2443)
+++ sea/optional/PrecompSeaGenerator.java	(working copy)
@@ -155,8 +155,8 @@
 		int maxLon = wholeArea.getMaxLong();
 
 		List<uk.me.parabola.imgfmt.app.Area> tiles = new ArrayList<uk.me.parabola.imgfmt.app.Area>();
-		for (int lon = getTileStart(minLon); lon < maxLon; lon += SeaGenerator.PRECOMP_RASTER) {
-			for (int lat = getTileStart(minLat); lat < maxLat; lat += SeaGenerator.PRECOMP_RASTER) {
+		for (int lon = SeaGenerator.getTileStart(minLon); lon < maxLon; lon += SeaGenerator.PRECOMP_RASTER) {
+			for (int lat = SeaGenerator.getTileStart(minLat); lat < maxLat; lat += SeaGenerator.PRECOMP_RASTER) {
 				uk.me.parabola.imgfmt.app.Area tile = new uk.me.parabola.imgfmt.app.Area(
 						Math.max(lat, minLat), Math.max(lon, minLon), Math.min(
 								lat + SeaGenerator.PRECOMP_RASTER, maxLat),
@@ -210,23 +210,7 @@
 		return new Area(path);
 	}
 
-	/**
-	 * Retrieves the start value of the precompiled tile.
-	 * @param value the value for which the start value is calculated
-	 * @return the tile start value
-	 */
-	private int getTileStart(int value) {
-		int rem = value % SeaGenerator.PRECOMP_RASTER;
-		if (rem == 0) {
-			return value;
-		} else if (value >= 0) {
-			return value - rem;
-		} else {
-			return value - SeaGenerator.PRECOMP_RASTER - rem;
-		}
-	}
 	
-	
 	/**
 	 * Creates the merger threads for the given tiles.
 	 * @param tiles the areas of the precompiled tiles
