Index: resources/help/en/options
===================================================================
--- resources/help/en/options	(revision 2274)
+++ resources/help/en/options	(working copy)
@@ -4,6 +4,8 @@
 
 General options:
 
+--precomp-sea
+
 --help=topic
 	Print help on the given topic.  If the topic is omitted then a
 	list of all the help topics is printed instead.
Index: src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java	(revision 2274)
+++ src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java	(working copy)
@@ -12,7 +12,12 @@
  */
 package uk.me.parabola.mkgmap.reader.osm;
 
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -31,7 +36,10 @@
 import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.LineClipper;
 import uk.me.parabola.mkgmap.osmstyle.StyleImpl;
+import uk.me.parabola.mkgmap.reader.osm.boundary.Boundary;
+import uk.me.parabola.mkgmap.reader.osm.boundary.BoundaryUtil;
 import uk.me.parabola.util.EnhancedProperties;
+import uk.me.parabola.util.Java2DConverter;
 
 /**
  * Code to generate sea polygons from the coastline ways.
@@ -63,6 +71,8 @@
 	private String[] coastlineFilenames;
 	private StyleImpl fbRules;
 	
+	private File precompSeaDir;
+	
 	/**
 	 * Sort out options from the command line.
 	 * Returns true only if the option to generate the sea is active, so that
@@ -70,6 +80,18 @@
 	 */
 	public boolean init(ElementSaver saver, EnhancedProperties props) {
 		this.saver = saver;
+		
+		String precompSea = props.getProperty("precomp-sea",null);
+		if (precompSea != null) {
+			precompSeaDir = new  File(precompSea);
+			if (precompSeaDir.exists()) {
+				return true; 
+			} else {
+				log.warn("Directory with precompiled sea does not exist: "+precompSeaDir);
+				precompSeaDir = null;
+			}
+		}
+		
 		String gs = props.getProperty("generate-sea", null);
 		boolean generateSea = gs != null;
 		if(generateSea) {
@@ -168,7 +190,7 @@
 		if(natural != null) {
 			if("coastline".equals(natural)) {
 				way.deleteTag("natural");
-				if (coastlineFilenames == null)
+				if (coastlineFilenames == null && precompSeaDir == null)
 					shoreline.add(way);
 			} else if (natural.contains(";")) {
 				// cope with compound tag value
@@ -187,7 +209,7 @@
 					way.deleteTag("natural");
 					if(others != null)
 						way.addTag("natural", others);
-					if (coastlineFilenames == null)
+					if (coastlineFilenames == null && precompSeaDir == null)
 						shoreline.add(way);
 				}
 			}
@@ -259,10 +281,40 @@
 		return joined;
 	}
 
+	private void addPrecompSea() {
+		Area seaBounds = saver.getBoundingBox();
+		
+		List<String> files = BoundaryUtil.getRequiredBoundaryFileNames(seaBounds);
+		for (String seaFile : files) {
+			try {
+				log.info("Opening sea file",seaFile);
+				DataInputStream stream = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(precompSeaDir,seaFile))));
+				List<Boundary> seaAreas = BoundaryUtil.loadBoundariesFromStream(stream, seaFile, seaBounds);
+				log.info(seaAreas.size(),"sea parts loaded.");
+				for (Boundary sb : seaAreas) {
+					List<List<Coord>> wayPoints = Java2DConverter.areaToShapes(sb.getArea());
+					for (List<Coord> points : wayPoints) {
+					Way seaWay =new Way(FakeIdGenerator.makeFakeId(),points);
+					seaWay.addTag("natural", sb.getTags().get("natural"));
+					saver.addWay(seaWay);
+					}
+				}
+			} catch (IOException exp) {
+				log.error("Cannot open boundary file "+seaFile);
+			}
+		}
+		
+	}
+	
 	/**
 	 * All done, process the saved shoreline information and construct the polygons.
 	 */
 	public void end() {
+		if (precompSeaDir != null) {
+			addPrecompSea();
+			return;
+		}
+		
 		Area seaBounds = saver.getBoundingBox();
 		if (coastlineFilenames == null) {
 			log.info("Shorelines before join", shoreline.size());
Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java	(revision 2274)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundarySaver.java	(working copy)
@@ -347,7 +347,7 @@
 		dos.flush();
 	}
 
-	private void saveToFile(String filekey, Boundary boundary) {
+	private synchronized void saveToFile(String filekey, Boundary boundary) {
 		try {
 			StreamInfo streamInfo = getStream(filekey);
 			if (streamInfo != null && streamInfo.isOpen()) {
Index: src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(revision 2274)
+++ src/uk/me/parabola/mkgmap/reader/osm/boundary/BoundaryUtil.java	(working copy)
@@ -510,11 +510,93 @@
 	 * @param fname the file name of the corresponding *.bnd file
 	 * @param searchBbox a bounding box or null. If not null, area info outside of this
 	 * bounding box is ignored. 
+	 * @return on success it returns a new BoundaryQuadTree, else null 
+	 * @throws IOException
+	 */
+	public static List<Boundary> loadBoundariesFromStream(InputStream stream, 
+			String fname,
+			uk.me.parabola.imgfmt.app.Area searchBbox) throws IOException{
+		try {
+			DataInputStream inpStream = new DataInputStream(
+					new BufferedInputStream(stream, 1024 * 1024));
+
+			try {
+				// 1st read the mkgmap release the boundary file is created by
+				String mkgmapRel = "?";
+				String firstId = inpStream.readUTF();
+				int format = UNKNOWN_DATA_FORMAT;
+				long createTime = inpStream.readLong();
+				if ("BND".equals(firstId) == false){
+					if (firstId.endsWith("_raw" ) || firstId.endsWith("_quadtree")){
+						// none-trunk version formats
+					}
+					else
+						format = LEGACY_DATA_FORMAT;
+					mkgmapRel = firstId;
+				}
+				else {
+					int headerLength = inpStream.readInt();
+					byte[] header = new byte[headerLength];
+					int bytesRead = 0;
+					while (bytesRead < headerLength) {
+						int nBytes = inpStream.read(header, bytesRead, headerLength-bytesRead);
+						if (nBytes<0) {
+							throw new IOException("Cannot read header with size "+headerLength);
+						} else {
+							bytesRead += nBytes;
+						}
+					}
+					
+					ByteArrayInputStream rawHeaderStream = new ByteArrayInputStream(header);
+					DataInputStream headerStream =new DataInputStream(rawHeaderStream);
+					String dataFormat = (rawHeaderStream.available() > 0 ? headerStream.readUTF() : "RAW");
+					int recordVersion = (rawHeaderStream.available() > 0 ? headerStream.readInt() : RAW_DATA_FORMAT_V1);
+					mkgmapRel = (rawHeaderStream.available() > 0 ? headerStream.readUTF() : "unknown");
+					if ("RAW".equals(dataFormat) && recordVersion == 1)
+						format = RAW_DATA_FORMAT_V1;
+					else if ("QUADTREE".equals(dataFormat) && recordVersion == 1)
+						format = QUADTREE_DATA_FORMAT_V1;
+				}
+				if (format == UNKNOWN_DATA_FORMAT)
+					throw new FormatException("Unsupported file format ");
+
+				if (log.isDebugEnabled()) {
+					log.debug("File created by mkgmap release",mkgmapRel,"at",new Date(createTime));
+				}
+				
+				
+				if (format == RAW_DATA_FORMAT_V1)
+					return readStreamRawFormat(inpStream, fname,searchBbox);
+				else 
+					return null;
+				
+			} catch (EOFException exp) {
+				// it's always thrown at the end of the file
+				//				log.error("Got EOF at the end of the file");
+			} 
+			catch (FormatException exp) {
+				log.error("Failed to read boundary file " + fname + " " + exp.getMessage());
+			} 
+			inpStream.close();
+		} finally {
+			if (stream != null)
+				stream.close();
+		}
+		return null;
+	}
+	
+	/**
+	 * Create and fill a BoundaryQuadTree. Read the header of the stream to detect
+	 * the proper reading routine for the different supported formats.
+	 * @param stream an already opened InputStream
+	 * @param fname the file name of the corresponding *.bnd file
+	 * @param searchBbox a bounding box or null. If not null, area info outside of this
+	 * bounding box is ignored. 
 	 * @param props properties to be used or null 
 	 * @return on success it returns a new BoundaryQuadTree, else null 
 	 * @throws IOException
 	 */
-	private static BoundaryQuadTree loadQuadTreeFromStream(InputStream stream, 
+	public static BoundaryQuadTree loadQuadTreeFromStream(InputStream stream, 
 			String fname,
 			uk.me.parabola.imgfmt.app.Area searchBbox, 
 			EnhancedProperties props)throws IOException{
Index: src/uk/me/parabola/util/Java2DConverter.java
===================================================================
--- src/uk/me/parabola/util/Java2DConverter.java	(revision 2274)
+++ src/uk/me/parabola/util/Java2DConverter.java	(working copy)
@@ -44,8 +44,7 @@
 	 */
 	public static Area createBoundsArea(uk.me.parabola.imgfmt.app.Area bbox) {
 		return new Area(new Rectangle(bbox.getMinLong(), bbox.getMinLat(),
-				bbox.getMaxLong() - bbox.getMinLong(), bbox.getMaxLat()
-						- bbox.getMinLat()));
+				bbox.getWidth(), bbox.getHeight()));
 	}
 
 	/**
