This version allows to store the contours in separate IMG files using the --dem-separate-img option. The name (8-digit number) of the IMG file will be the original mapname + 10000000.

I also replaced orthodromicDistance by quickDistance (I only use the distance calculation to find out if I should refine a contour line segment by splitting it).

Best wishes
Christian
Index: src/uk/me/parabola/mkgmap/Option.java
===================================================================
--- src/uk/me/parabola/mkgmap/Option.java       (Revision 1078)
+++ src/uk/me/parabola/mkgmap/Option.java       (Arbeitskopie)
@@ -34,7 +34,7 @@
                }
        }
 
-       protected Option(String option, String value) {
+       public Option(String option, String value) {
                this.option = option;
                this.value = value;
        }
Index: src/uk/me/parabola/mkgmap/CommandArgsReader.java
===================================================================
--- src/uk/me/parabola/mkgmap/CommandArgsReader.java    (Revision 1078)
+++ src/uk/me/parabola/mkgmap/CommandArgsReader.java    (Arbeitskopie)
@@ -129,7 +129,7 @@
         * @param option The option name.
         * @param value Its value.
         */
-       private void addOption(String option, String value) {
+       public void addOption(String option, String value) {
                CommandOption opt = new CommandOption(option, value);
                addOption(opt);
        }
@@ -181,13 +181,21 @@
                return arglist.iterator();
        }
 
+        public ArgList getArgList() {
+               return arglist;
+       }
+
+        public EnhancedProperties getArgs() {
+           return args;
+       }
+
        /**
         * Read a config file that contains more options.  When the number of
         * options becomes large it is more convenient to place them in a file.
         *
         * @param filename The filename to obtain options from.
         */
-       private void readConfigFile(String filename) {
+       public void readConfigFile(String filename) {
                Options opts = new Options(new OptionProcessor() {
                        public void processOption(Option opt) {
                                log.debug("incoming opt", opt.getOption(), 
opt.getValue());
@@ -206,14 +214,14 @@
         * the argument to be processed in order.  Options can be intersperced 
with
         * filenames.  The options take effect where they appear.
         */
-       interface ArgType {
+       public interface ArgType {
                public abstract void processArg();
        }
 
        /**
         * A filename.
         */
-       class Filename implements ArgType {
+       public class Filename implements ArgType {
                private final String name;
                private boolean useFilenameAsMapname = true;
 
@@ -270,7 +278,7 @@
        /**
         * An option argument.  A key value pair.
         */
-       class CommandOption implements ArgType {
+       public class CommandOption implements ArgType {
                private final Option option;
 
                private CommandOption(Option option) {
@@ -297,7 +305,7 @@
        /**
         * The arguments are held in this list.
         */
-       class ArgList implements Iterable<CommandArgsReader.ArgType> {
+       public class ArgList implements Iterable<CommandArgsReader.ArgType> {
                private final List<ArgType> alist;
 
                private int filenameCount;
Index: src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java      
(Revision 1078)
+++ src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java      
(Arbeitskopie)
@@ -27,12 +27,14 @@
 import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.trergn.Overview;
+import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
 import uk.me.parabola.mkgmap.general.MapDataSource;
 import uk.me.parabola.mkgmap.general.MapDetails;
 import uk.me.parabola.mkgmap.general.MapLine;
 import uk.me.parabola.mkgmap.general.MapPoint;
 import uk.me.parabola.mkgmap.general.MapShape;
 import uk.me.parabola.mkgmap.general.RoadNetwork;
+import uk.me.parabola.mkgmap.reader.dem.DEM;
 import uk.me.parabola.util.Configurable;
 import uk.me.parabola.util.EnhancedProperties;
 
@@ -118,6 +120,11 @@
                return configProps;
        }
 
+        public MapDetails getMapper()
+        {
+               return mapper;
+       }
+
        /**
         * We add the background polygons if the map is not transparent.
         */
@@ -146,5 +153,8 @@
                        // the overview section.
                        mapper.addShape(background);
                }
+               if (getConfig().getProperty("contours", false)) {               
    
+                   DEM.createContours((LoadableMapDataSource) this, 
getConfig());
+               }
        }
 }
Index: src/uk/me/parabola/mkgmap/reader/osm/Way.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/Way.java       (Revision 1078)
+++ src/uk/me/parabola/mkgmap/reader/osm/Way.java       (Arbeitskopie)
@@ -30,12 +30,18 @@
  */
 public class Way extends Element {
 
-       private final List<Coord> points = new ArrayList<Coord>();
+        private final List<Coord> points;
 
        public Way(long id) {
+                points = new ArrayList<Coord>();
                setId(id);
        }
 
+        public Way(long id, List<Coord> points) {
+               this.points = points;
+               setId(id);
+       }
+
        /**
         * Get the points that make up the way.  We attempt to re-order the 
segments
         * and return a list of points that traces the route of the way.
/*
 * Copyright (C) 2009 Christian Gawron
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 * 
 * Author: Christian Gawron
 * Create date: 03-Jul-2009
 */
package uk.me.parabola.mkgmap.reader.dem;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.MappedByteBuffer;

import java.util.Vector;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;

import com.sun.media.jai.codec.*;
import javax.media.jai.*;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.Raster;
import java.awt.image.DataBuffer;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.FileSystemParam;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.map.Map;

import uk.me.parabola.mkgmap.build.MapBuilder;
import uk.me.parabola.mkgmap.general.MapDetails;
import uk.me.parabola.mkgmap.general.MapPoint;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
import uk.me.parabola.mkgmap.reader.MapperBasedMapDataSource;
import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
import uk.me.parabola.mkgmap.reader.osm.Style;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.mkgmap.osmstyle.StyleImpl;
import uk.me.parabola.mkgmap.osmstyle.StyledConverter;
import uk.me.parabola.mkgmap.osmstyle.eval.SyntaxException;
import uk.me.parabola.util.EnhancedProperties;

import static java.nio.channels.FileChannel.MapMode.READ_ONLY;

/**
 * Create contour lines using an algorithm similar to that described in 
 * <a 
href="http://mapcontext.com/autocarto/proceedings/auto-carto-5/pdf/an-adaptive-grid-contouring-algorithm.pdf";>An
 Adaptive Grid Contouring Algorithm</a> by Downing and Zoraster. 
 */
public abstract class DEM 
{
    final static double epsilon = 1e-9;
    final static double delta = 1.5;
    final static int maxPoints=200000;
    final static double minDist = 15;
    final static double maxDist = 21;
    final static double step = 0.01;
    final static double semiMajorAxis = 6378137.0;
    final static double inverseFlattening = 298.257223563;

    static int M=1200;
    static int N=M;
    static double res = 1.0/N;
    static int id = -1;

    short values[] = null;
    int lat;
    int lon;
    
    abstract double ele(int x, int y);
    abstract void read(int minLon, int minLat, int maxLon, int maxLat);
    abstract void serializeCopyRight(Writer out) throws IOException;

    public static void createContours(LoadableMapDataSource mapData, 
EnhancedProperties config)
    {
        Area bounds = mapData.getBounds();

        double minLat = Utils.toDegrees(bounds.getMinLat());
        double minLon = Utils.toDegrees(bounds.getMinLong());
        double maxLat = Utils.toDegrees(bounds.getMaxLat());
        double maxLon = Utils.toDegrees(bounds.getMaxLong());
        
        System.out.printf("bounds: %f %f %f %f\n", minLat, minLon, maxLat, 
maxLon);
        DEM data;
        String demType = config.getProperty("dem-type", "CGIAR");

        String dataPath;
        if (demType.equals("ASTER")) {
            dataPath = config.getProperty("dem-path", "ASTER");
            data = new ASTERGeoTiff(dataPath, minLat, minLon, maxLat, maxLon);
        }
        else if (demType.equals("CGIAR")) {
            dataPath = config.getProperty("dem-path", "CGIAR");
            data = new CGIARGeoTiff(dataPath, minLat, minLon, maxLat, maxLon);
        }
        else {
            dataPath = config.getProperty("dem-path", "SRTM");
            data = new HGTDEM(dataPath, minLat, minLon, maxLat, maxLon);
        }
        
        Isolines lines = data.new Isolines(data, minLat, minLon, maxLat, 
maxLon);
        int increment = config.getProperty("dem-increment", 10);

        double minHeight = lines.getMinHeight();
        double maxHeight = lines.getMaxHeight();
        int maxLevels = config.getProperty("dem-maxlevels", 100);
        while ((maxHeight-minHeight)/increment > maxLevels)
            increment *= 2;

        String loc = config.getProperty("style-file");
        if (loc == null)
            loc = config.getProperty("map-features");
        String name = config.getProperty("style");
        
        if (loc == null && name == null)
            name = "default";

        LoadableMapDataSource dest = mapData;
        if (config.getProperty("dem-separate-img", false)) {
            dest = new DEMMapDataSource(mapData, config);
        }

        
        OsmConverter converter;
        try {
            Style style = new StyleImpl(loc, name);
            style.applyOptionOverride(config);
            
            converter = new StyledConverter(style, ((MapperBasedMapDataSource) 
dest).getMapper(), config);
        } catch (SyntaxException e) {
            System.err.println("Error in style: " + e.getMessage());
            throw new ExitException("Could not open style " + name);
        } catch (FileNotFoundException e) {
            String name1 = (name != null)? name: loc;
            throw new ExitException("Could not open style " + name1);
        }
        
        for (int level=0; level<maxHeight; level+=increment) {
            if (level < minHeight) continue;

            // create isolines
            lines.addLevel(level);
        
            for (Isolines.Isoline line : lines.isolines) {
                Way way = new Way(id--, line.points);
                way.addTag("contour", "elevation");
                way.addTag("ele", String.format("%d", (int) line.level));
                converter.convertWay(way);
            }
            lines.isolines.clear();
        }
        
        if (config.getProperty("dem-separate-img", false)) {
            MapBuilder builder = new MapBuilder();
            builder.config(config);

            FileSystemParam params = new FileSystemParam();
            params.setMapDescription("contour lines");
            long mapName = Integer.valueOf(config.getProperty("mapname", 
"63240000"));
            try {
                Map map = Map.createMap(String.format("%08d", 
mapName+10000000), params);
                builder.makeMap(map, dest);
                map.close();
            }
            catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    public static class HGTDEM extends DEM
    {
        MappedByteBuffer buffer = null;

        public HGTDEM(String dataPath, double minLat, double minLon, double 
maxLat, double maxLon)
        {
            this.lat = (int) minLat;
            this.lon = (int) minLon;
            if (maxLat > lat+1 || maxLon > lon+1)
                throw new RuntimeException("Area too large (must not span more 
than one SRTM file)");

            String northSouth = lat < 0 ? "S" : "N";
            String eastWest = lon > 0 ? "E" : "W";
            String fileName = String.format("%s/%s%02d%s%03d.hgt", dataPath, 
                                            northSouth, lat < 0 ? -lat : lat, 
                                            eastWest, lon < 0 ? -lon : lon);
            try {
                FileInputStream is = new FileInputStream(fileName);
                buffer = is.getChannel().map(READ_ONLY, 0, 2*(M+1)*(M+1));
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public  void read(int minLon, int minLat, int maxLon, int maxLat)
        {
        }

        public double ele(int x, int y)
        {
            return buffer.getShort(2*((M-y)*(M+1)+x))+delta;
        }

        public void serializeCopyRight(Writer out) throws IOException
        {
            out.write("  <copyright>\n");
            out.write("  Contour lines generated from DEM data by NASA\n");
            out.write("  </copyright>\n");
        }
    }

    public static class CGIARGeoTiff extends DEM
    {
        Raster raster;
        String fileName;
        int minLat, minLon, maxLat, maxLon;
        PlanarImage image;

        public CGIARGeoTiff(String dataPath, double minLat, double minLon, 
double maxLat, double maxLon)
        {
            this.lat = ((int) (minLat/5))*5;
            this.lon = ((int) (minLon/5))*5;
            if (maxLat > lat+5 || maxLon > lon+5)
                throw new RuntimeException("Area too large (must not span more 
than one CGIAR GeoTIFF)");

            int tileX, tileY;
            tileX = (180 + lon)/5 + 1;
            tileY = (60 - lat)/5;
            this.fileName = String.format("%s/srtm_%02d_%02d.tif", dataPath, 
tileX, tileY);
            init();
        }

        public void serializeCopyRight(Writer out) throws IOException
        {
            out.write("  <copyright>\n");
            out.write("  Contour lines generated from improved SRTM data by 
CIAT-CSI (see http://srtm.csi.cgiar.org)\n");
            out.write("  </copyright>\n");
        }
        
        public  void read(int minLon, int minLat, int maxLon, int maxLat)
        {
            this.minLon = minLon;
            this.minLat = minLat;
            this.maxLon = maxLon;
            this.maxLat = maxLat;
            raster = image.getData(new java.awt.Rectangle(minLon, 
6000-maxLat-1, maxLon-minLon+1, maxLat-minLat+1));
            System.out.printf("read: %d %d %d %d\n", minLon, 6000-maxLat-1, 
maxLon-minLon+1, maxLat-minLat+1);
        }

        void init()
        {
            System.out.printf("CGIAR GeoTIFF: %s\n", fileName);
            N = 6000;
            M = 6000;
            res = 5.0/M;
            
            try {
                SeekableStream s = new FileSeekableStream(fileName);
                ParameterBlock pb = new ParameterBlock();
                pb.add(s);
                
                TIFFDecodeParam param = new TIFFDecodeParam();
                pb.add(param);
                
                RenderedOp op = JAI.create("tiff", pb);
                image = op.createInstance();
                System.out.printf("Image: %d %d %d %d\n", image.getWidth(), 
image.getHeight(), 
                                  image.getNumXTiles(), image.getNumYTiles());
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public double ele(int x, int y)
        {
            try {
                int elevation = raster.getPixel(x, 6000-y-1, (int[])null)[0];
                return elevation+delta;
            }
            catch (ArrayIndexOutOfBoundsException ex) {
                System.out.printf("ele: (%d, %d) (%d, %d, %d, %d)  %s\n", 
                                  x, 6000-y-1, 
                                  raster.getMinX(), raster.getMinY(), 
                                  raster.getWidth(), raster.getHeight(), 
ex.toString());
                throw ex;
            }
        }
    }

    public static class ASTERGeoTiff extends DEM
    {
        Raster raster;
        String fileName;
        int minLat, minLon, maxLat, maxLon;
        PlanarImage image;

        public ASTERGeoTiff(String dataPath, double minLat, double minLon, 
double maxLat, double maxLon)
        {
            this.lat = (int) minLat;
            this.lon = (int) minLon;
            if (maxLat > lat+1 || maxLon > lon+1)
                throw new RuntimeException("Area too large (must not span more 
than one ASTER GeoTIFF)");

            String northSouth = lat < 0 ? "S" : "N";
            String eastWest = lon > 0 ? "E" : "W";
            fileName = String.format("%s/ASTGTM_%s%02d%s%03d_dem.tif", 
dataPath, 
                                     northSouth, lat < 0 ? -lat : lat, 
                                     eastWest, lon < 0 ? -lon : lon);
            init();
        }

        public void serializeCopyRight(Writer out) throws IOException
        {
            out.write("  <copyright>\n");
            out.write("  Contour lines generated from DGM data by ASTER (see 
https://wist.echo.nasa.gov/~wist/api/imswelcome)\n");
            out.write("  </copyright>\n");
        }
        
        public  void read(int minLon, int minLat, int maxLon, int maxLat)
        {
            this.minLon = minLon;
            this.minLat = minLat;
            this.maxLon = maxLon;
            this.maxLat = maxLat;
            raster = image.getData(new java.awt.Rectangle(minLon, 
3601-maxLat-1, maxLon-minLon+1, maxLat-minLat+1));
            System.out.printf("read: %d %d %d %d\n", minLon, 3601-maxLat-1, 
maxLon-minLon+1, maxLat-minLat+1);
        }

        void init()
        {
            System.out.printf("ASTER GeoTIFF: %s\n", fileName);
            N = 3600;
            M = 3600;
            res = 1.0/M;
            
            try {
                SeekableStream s = new FileSeekableStream(fileName);
                ParameterBlock pb = new ParameterBlock();
                pb.add(s);
                
                TIFFDecodeParam param = new TIFFDecodeParam();
                pb.add(param);
                
                RenderedOp op = JAI.create("tiff", pb);
                image = op.createInstance();
                System.out.printf("Image: %d %d %d %d\n", image.getWidth(), 
image.getHeight(), 
                                  image.getNumXTiles(), image.getNumYTiles());
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public double ele(int x, int y)
        {
            try {
                int elevation = raster.getPixel(x, 3601-y-1, (int[])null)[0];
                return elevation+delta;
            }
            catch (ArrayIndexOutOfBoundsException ex) {
                System.out.printf("ele: (%d, %d) (%d, %d, %d, %d)  %s\n", 
                                  x, 3601-y-1, 
                                  raster.getMinX(), raster.getMinY(), 
                                  raster.getWidth(), raster.getHeight(), 
ex.toString());
                throw ex;
            }
        }
    }

    private static void debug(String format, Object ... args)
    {
        //System.out.printf(format + "\n", args);
    }


    int lastXi = -1;
    int lastYi = -1;

    final static int bcInv[][]= {
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 
        { -3, 0, 0, 3, 0, 0, 0, 0, -2, 0, 0, -1, 0, 0, 0, 0},
        { 2, 0, 0, -2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, 
        { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 
        { 0, 0, 0, 0, -3, 0, 0, 3, 0, 0, 0, 0, -2, 0, 0, -1}, 
        { 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0, 0, 1, 0, 0, 1}, 
        { -3, 3, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
        { 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0}, 
        { 9, -9, 9, -9, 6, 3, -3, -6, 6, -6, -3, 3, 4, 2, 1, 2}, 
        { -6, 6, -6, 6, -4, -2, 2, 4, -3, 3, 3, -3, -2, -1, -1, -2}, 
        { 2, -2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
        { 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0}, 
        { -6, 6, -6, 6, -3, -3, 3, 3, -4, 4, 2, -2, -2, -2, -1, -1}, 
        { 4, -4, 4, -4, 2, 2, -2, -2, 2, -2, -2, 2, 1, 1, 1, 1}
    };

    static int lastId=1000000000;
    static double lastX = 0;
    static double lastY = 0;

    static int edge[] = new int[2];
    
    static int off0[][] = {{ 0,  0},
                           { 0,  0},
                           { 0,  1},
                           { 1,  1}};
    
    static int off1[][] = {{ 1,  0},
                           { 0,  1},
                           { 1,  1},
                           { 1,  0}};
    
    static int brd[] = { 1, 2, 4, 8 };
    static int inv[] = { 4, 8, 1, 2 };
    
    static int rev[] = { 2, 3, 0, 1 };
    
    static int mov[][] = {{ 0, -1},
                          {-1,  0},
                          { 0,  1},
                          { 1,  0}};


    double bc[][] = new double[4][4];
    double bc_y[] = new double[4];
    double bc_y1[] = new double[4];
    double bc_y2[] = new double[4];
    double bc_y12[] = new double[4];
    double bc_Coeff[] = new double[16];
    double bc_x[] = new double[16];

    private void recalculateCoefficients(int xi, int yi)
    {
        double v00, vp0, v0p, vpp;
        double vm0, v0m, vpm, vmp, vmm, vmP, vPm; 
        double vP0, v0P, vPp, vpP, vPP;

        v00 = ele(xi, yi);
        v0p = ele(xi, yi+1);
        vpp = ele(xi+1, yi+1);
        vp0 = ele(xi+1, yi);

        vm0 = ele(xi-1, yi);
        v0m = ele(xi, yi-1);
        vmp = ele(xi-1, yi+1);
        vpm = ele(xi+1, yi-1);
        vmm = ele(xi-1, yi-1);
        vmP = ele(xi+2, yi-1);
        vPm = ele(xi-1, yi+2);

        vP0 = ele(xi+2, yi);
        v0P = ele(xi, yi+2);
        vPp = ele(xi+2, yi+1);
        vpP = ele(xi+1, yi+2);
        vPP = ele(xi+2, yi+2);

        bc_y[0] = v00;
        bc_y[1] = vp0;
        bc_y[2] = vpp;
        bc_y[3] = v0p;

        bc_y1[0] = (vp0-vm0)/2;
        bc_y1[1] = (vP0-v00)/2;
        bc_y1[2] = (vPp-v0p)/2;
        bc_y1[3] = (vpp-vmp)/2;

        bc_y2[0] = (v0p-v0m)/2;
        bc_y2[1] = (vpp-vpm)/2;
        bc_y2[2] = (vpP-vp0)/2;
        bc_y2[3] = (v0P-v00)/2;

        bc_y12[0] = (vpp - vpm - vmp + vmm) / 4;
        bc_y12[0] = (vPp - vPm - v0p + v0m) / 4;
        bc_y12[2] = (vPP - vP0 - v0P + v00) / 4;
        bc_y12[0] = (vpP - vp0 - vmP + vm0) / 4;

        int j, i;
        double s;

        for (i=0; i<4; i++) 
        {
            bc_x[i]=bc_y[i];
            bc_x[i+4]=bc_y1[i];
            bc_x[i+8]=bc_y2[i];
            bc_x[i+12]=bc_y12[i];
        }

        for (i=0; i<16; i++) 
        {
            s = 0;
            for (int k=0; k<16; k++) s += bcInv[i][k]*bc_x[k];
            bc_Coeff[i] = s;
        }

        int l = 0;
        for (i=0; i<4; i++)
            for (j=0; j<4; j++) 
                bc[i][j] = bc_Coeff[l++];
    }

    public double gradient(double lat, double lon, double[] grad)
    {
        grad[0] = 0;
        grad[1] = 0;

        double x = (lon-this.lon)/res;
        double y = (lat-this.lat)/res;
        
        int xi = (int) x;
        int yi = (int) y;

        if (lastXi != xi || lastYi != yi)
        {
            debug("new Cell for interpolation: %d %d", xi, yi);
            recalculateCoefficients(xi, yi);
            lastXi = xi;
            lastYi = yi;
        }

        double t = x - xi;
        double u = y - yi;

        if  (xi < 0 || xi > N+1 || yi < 0 || yi > N+1)
            throw new IndexOutOfBoundsException(String.format("(%f, %f)->(%d, 
%d)", lat, lon, xi, yi));
        
        double val = 0;
        for (int i=3; i>=0; i--) 
        {
            val = t*val + ((bc[i][3]*u + bc[i][2])*u + bc[i][1])*u + bc[i][0];
            grad[0] = u*grad[0] + (3*bc[3][i]*t + 2*bc[2][i])*t + bc[1][i];
            grad[1] = t*grad[1] + (3*bc[i][3]*t + 2*bc[i][2])*t + bc[i][1];
        }

        return val;
    }

    public double elevation(double lat, double lon)
    {
        double x = (lon-this.lon)/res;
        double y = (lat-this.lat)/res;
        
        int xi = (int) x;
        int yi = (int) y;

        if (lastXi != xi || lastYi != yi)
        {
            debug("new Cell for interpolation: %d %d", xi, yi);
            recalculateCoefficients(xi, yi);
            lastXi = xi;
            lastYi = yi;
        }

        double t = x - xi;
        double u = y - yi;

        if  (xi < 0 || xi > N+1 || yi < 0 || yi > N+1)
            throw new IndexOutOfBoundsException(String.format("(%f, %f)->(%d, 
%d)", lat, lon, xi, yi));
        
        double val = 0;
        for (int i=3; i>=0; i--) 
        {
            val = t*val + ((bc[i][3]*u + bc[i][2])*u + bc[i][1])*u + bc[i][0];
        }

        return val;
    }

    public double elevation(int x, int y)
    {
        if (x < 0 || x > N || y < 0 || y > N)
            throw new IndexOutOfBoundsException(String.format("elevation: %d 
%d", x, y));
        return ele(x, y);
    }


    class Isolines
    {
        DEM data;
        int minX;
        int maxX; 
        int minY; 
        int maxY;

        double min;
        double max;

        Vector<Isoline> isolines = new Vector<Isoline>();

        class Isoline
        {
            int id;
            Vector<Coord> points;
            double level;
            boolean isClosed;
            
            private Isoline(double level)
            {
                this.level = level;
                isClosed = false;
                id = lastId++;
                points = new Vector<Coord>();
            }

            private class Edge implements Brent.Function
            {
                double x0, y0, x1, y1;
                Edge(double x0, double y0, double x1, double y1)
                {
                    this.x0 = x0;
                    this.y0 = y0;
                    this.x1 = x1;
                    this.y1 = y1;
                } 
                
                public double eval(double d)
                {
                    double f = data.elevation(x0 + d * (x1-x0), y0 + d * 
(y1-y0)) - level; 
                    //System.out.printf("evalEdge: %f %f\n", d, f); 
                    return f;
                }
            }

            private class FN implements Brent.Function
            {
                double x0, y0;
                double dx, dy;

                public void setParameter(double x0, double y0, double dx, 
double dy)
                {
                    this.x0 = x0;
                    this.y0 = y0;
                    this.dx = dx;
                    this.dy = dy;
                }

                public double eval(double t) 
                { 
                    double f = data.elevation(y0+t*dy, x0+t*dx) - level; 
                    return f; 
                }
            }
            
            private FN fn = new FN();

            double grad[] = new double[2];
            double px[] = new double[4];
            double py[] = new double[4];
            int edges[] = new int[4];

            boolean addCell(Position p, int direction)
            {
                debug("addCell: %f %d %d %d %d", level, p.ix, p.iy, p.edge, 
direction);
                
                int c = 0;
                for (int k=0; k<4; k++)
                {
                    if (k == p.edge)
                        continue;

                    int x0 = p.ix + off0[k][0];
                    int y0 = p.iy + off0[k][1];
                    int x1 = p.ix + off1[k][0];
                    int y1 = p.iy + off1[k][1];
                    
                    double l0 = elevation(x0, y0) - level;
                    double l1 = elevation(x1, y1) - level;
                    
                    if (Math.abs(l1) < epsilon || l0*l1 < 0)
                    {
                        edges[c] = k;

                        Brent.Function f = new Edge(data.lat + y0 * DEM.res, 
data.lon + x0 * DEM.res, data.lat + y1 * DEM.res, data.lon + x1 * DEM.res);
                        double f0 = elevation(x0, y0) - level;
                        double f1 = elevation(x1, y1) - level;
                        double delta;
                        
                        if (Math.abs(1) < epsilon)
                        {
                            delta = 1;
                        }
                        else if (Math.abs(f0) < epsilon)
                            throw new RuntimeException("implementation error!");
                        else
                            delta = Brent.zero(f, epsilon, 1-epsilon);
                        
                        px[c] = data.lon + (x0+delta*(x1-x0))*DEM.res;
                        py[c] = data.lat + (y0+delta*(y1-y0))*DEM.res;
                        c++;
                    }
                }


                if (c == 1)
                {
                    p.edge = edges[0];

                    double px0 = p.x;
                    double py0 = p.y;
                    p.x = px[0];
                    p.y = py[0];
                    double px1 = p.x;
                    double py1 = p.y;
                    
                    double xMin = data.lon + p.ix * DEM.res;
                    double xMax = xMin + DEM.res;
                    double yMin = data.lat + p.iy * DEM.res;
                    double yMax = yMin + DEM.res;

                    refineAdaptively(xMin, yMin, xMax, yMax, px0, py0, px1, 
py1, direction, maxDist);

                    addPoint(p.x, p.y, direction);
                    p.moveCell();
                    return true;
                }
                else
                {
                    debug("addCellByStepping: %d", c);
                    return addCellByStepping(p, direction, c, edges, px, py);
                }               
            }

            private void refineAdaptively(double xMin, double yMin, double 
xMax, double yMax, 
                                          double x0, double y0, double x1, 
double y1, 
                                          int direction, double maxDist)
            {
                double dist = quickDistance(x0, y0, x1, y1);
                if (dist > maxDist)
                {
                    double dx = x1-x0;
                    double dy = y1-y0;
                    double xm, ym, f0, f1, t0, t1, t;
                    Brent.Function f;
                    
                    xm = x0 + 0.5*dx;
                    ym = y0 + 0.5*dy;
                    double n = Math.sqrt(dx*dx+dy*dy);
                    fn.setParameter(xm, ym, -dy/n, dx/n);
                    f = fn;
                    t0 = -0.05*res;
                    t1 = 0.05*res;
                    f0 = f.eval(t0);
                    f1 = f.eval(t1);
                
                    int count = 0;
                    while (f0 * f1 > 0 && count++ < 20) {
                        if ((count & 1) > 0)
                            t0 -= 0.05*res;
                        else
                            t1 += 0.05*res; 
                        f0 = f.eval(t0);
                        f1 = f.eval(t1);
                        debug("refine: %f %f %f %f", t0, t1, f0, f1);
                    }
                        
                    if (f0 * f1 < 0)
                    {
                        t = Brent.zero(f, t0, t1);
                        xm -= t*dy;
                        ym += t*dx;
                    }
                    else
                    {
                        debug("refine failed: %f %f %f %f", t0, t1, f0, f1);
                        if (false) throw new 
RuntimeException(String.format("refine failed: %f %f %f %f %f %f %f %f", xMin, 
yMin, xMax, yMax, x0, y0, x1, y1));
                        return;
                    }

                    if (xm > xMin && xm < xMax && ym > yMin && ym < yMax)
                        refineAdaptively(xMin, yMin, xMax, yMax, x0, y0, xm, 
ym, direction, maxDist*1.1);
                    addPoint(xm, ym, direction);
                    if (xm > xMin && xm < xMax && ym > yMin && ym < yMax)
                        refineAdaptively(xMin, yMin, xMax, yMax, xm, ym, x1, 
y1, direction, maxDist*1.1);
                }
            }

            boolean addCellByStepping(Position p, int direction, int numEdges, 
int[] edges, double[] px, double[] py)
            {
                debug("addCellByStepping: %f %d %d %d %d", level, p.ix, p.iy, 
p.edge, direction);


                double xMin = data.lon + p.ix * DEM.res;
                double xMax = xMin + DEM.res;
                double yMin = data.lat + p.iy * DEM.res;
                double yMax = yMin + DEM.res;

                double dt, t0, t1;
                double f0, f1;
                boolean edgeHit = false;
                double h[] = new double[4];
                
                int dir;
                double n2 = Math.sqrt(1.0/(grad[0]*grad[0] + grad[1]*grad[1]));
                double dx;
                double dy;

                int count=0;
                int iMin = -1;
                double md = 5000;
                
                for (int i=0; i<numEdges; i++) {
                    gradient(p.y, p.x, grad);
                    double dist = quickDistance(p.x, p.y, px[i], py[i]);
                    debug("distance %d: %f", i, dist);
                    
                    if (dist < md && (visited[p.iy*(N+1)+p.ix] & brd[edges[i]]) 
== 0) {
                        md = dist;
                        iMin = i;
                    }
                }

                p.edge = edges[iMin];
                
                double px0 = p.x;
                double py0 = p.y;
                p.x = px[iMin];
                p.y = py[iMin];
                double px1 = p.x;
                double py1 = p.y;
                
                xMin = data.lon + p.ix * DEM.res;
                xMax = xMin + DEM.res;
                yMin = data.lat + p.iy * DEM.res;
                yMax = yMin + DEM.res;
                
                refineAdaptively(xMin, yMin, xMax, yMax, px0, py0, px1, py1, 
direction, maxDist);
                
                addPoint(p.x, p.y, direction);
                p.moveCell();
                return true;
            }

            private void addPoint(double x, double y, int direction)
            {
                double dist = quickDistance(x, y, lastX, lastY);
                debug("addPoint: %f %f %f", x, y, dist);
                
                if (dist > minDist)
                {
                    if (direction > 0)
                        points.add(0, new Coord(y, x));
                    else
                        points.add(points.size(), new Coord(y, x));
                    lastX = x;
                    lastY = y;
                }
            }

            private void close()
            {
                points.add(points.size(), points.get(0));
                isClosed = true;
            }

            private void addMove(int x0, int y0, int x1, int y1, int direction)
            {
                double x;
                double y;
                
                if (x0 < minX || x0 >= maxX || y0 < minY || y0 >= maxY)
                    return;
                double l0 = data.elevation(x0, y0);
                double l1 = data.elevation(x1, y1);
                debug("addMove: %d %d %d %d %.2f %.2f %d", x0, y0, x1, y1, l0, 
l1, direction);
                
                if ((l0 < level && l1 < level) || (l0 > level && l1 > level))
                    throw new RuntimeException("implementation error");
                
                if (l0 == level)
                {
                    x = data.lon + x0 * DEM.res;
                    y = data.lat + y0 * DEM.res;
                }
                else
                {
                    double delta = (l0-level)/(l0-l1);
                    x = data.lon + (x0 + delta * (x1-x0)) * DEM.res;
                    y = data.lat + (y0 + delta * (y1-y0)) * DEM.res;
                }
                double dist = quickDistance(x, y, lastX, lastY);
                debug("levels: %d %d %f, %d %d %f: %f %f %f", x0, y0, l0, x1, 
y1, l1, x, y, dist);
                
                if (dist > 1)
                {
                    if (direction > 0)
                        points.add(0, new Coord(y, x));
                    else
                        points.add(points.size(), new Coord(y, x));
                    lastX = x;
                    lastY = y;
                }
            }
        }
         
        public Isolines(DEM data, int minX, int maxX, int minY, int maxY)
        {
            this.data = data;
            this.minX = minX;
            this.maxX = maxX;
            this.minY = minY;
            this.maxY = maxY;
            
            init();
        }

        public Isolines(DEM data, double  minLat, double minLon, double maxLat, 
double maxLon)
        {
            System.out.printf("init: %f %f %f %f\n", minLat, minLon, maxLat, 
maxLon);

            this.data = data;
            this.minX = (int) ((minLon-data.lon)/data.res);
            this.minY = (int) ((minLat-data.lat)/data.res);
            this.maxX = (int) ((maxLon-data.lon)/data.res);
            this.maxY = (int) ((maxLat-data.lat)/data.res);
            
            init();
        }

        private void init()
        {
            System.out.printf("init: %d %d %d %d\n", minX, minY, maxX, maxY);
            data.read(minX-2, minY-2, maxX+2, maxY+2); 
            // we need some overlap for bicubic interpolation
            max = -1000;
            min = 10000;
            for (int i=minX; i<maxX; i++)
                for (int j=minY; j<maxY; j++)
                {
                    if (data.elevation(i, j) < min) min = data.elevation(i, j);
                    if (data.elevation(i, j) > max) max = data.elevation(i, j);
                }
        
            debug("min: %f, max: %f\n", min, max);
        }
        
        double getMinHeight()
        {
            return min;
        }

        double getMaxHeight()
        {
            return max;
        }

        public void addLevels(int start, int increment)
        {
            for (int level=start; level<max; level+=increment)
                addLevel(level);
        }

        private class Edge implements Brent.Function
        {
            double x0, y0, x1, y1, level;
            Edge(double x0, double y0, double x1, double y1, double level)
            {
                this.x0 = x0;
                this.y0 = y0;
                this.x1 = x1;
                this.y1 = y1;
                this.level = level;
            } 
            
            public double eval(double d)
            {
                double f = data.elevation(x0 + d * (x1-x0), y0 + d * (y1-y0)) - 
level; 
                //System.out.printf("evalEdge: %f %f\n", d, f); 
                return f;
            }
        }

        private class Position
        {
            int ix, iy;
            double x, y;
            int edge;

            Position(int ix, int iy, double x, double y, int edge)
            {
                this.ix = ix;
                this.iy = iy;
                this.x = x;
                this.y = y;
                this.edge = edge;
            } 

            Position(Position p)
            {
                this.ix = p.ix;
                this.iy = p.iy;
                this.x = p.x;
                this.y = p.y;
                this.edge = p.edge;
            } 

            void markEdge()
            {
                debug("marking edge: %d %d %d %d", ix, iy, edge, brd[edge]);
                visited[iy*(N+1)+ix] |= brd[edge];
            }

            void moveCell()
            {
                markEdge();
                ix += mov[edge][0];
                iy += mov[edge][1];
                edge = rev[edge];
                markEdge();
            }
        }

        byte visited[] = new byte[(N+1)*(N+1)];

        public void addLevel(double level)
        {
            if (level < min || level > max)
                return;
            
            System.out.printf("addLevel: %f\n", level);
            java.util.Arrays.fill(visited, (byte) 0);

            for (int y=minY; y<maxY; y++)
            {
                for (int x=minX; x<maxX; x++)
                {
                    byte v = 0;
                    // Mark the borders of the cell, represented by the four 
points (i, j), (i+1, j), (i, j+1), (i+1, j+1), 
                    // which are intersected by the contour. The values are:
                    // 1: top
                    // 2: left
                    // 4: bottom
                    // 8: right
                    if (data.elevation(x, y) >= level)
                    {
                        if (data.elevation(x+1, y) < level) { v |= 1; }
                        if (data.elevation(x, y+1) < level) { v |= 2; }
                    }
                    else
                    {
                        if (data.elevation(x+1, y) > level) { v |= 1; }
                        if (data.elevation(x, y+1) > level) { v |= 2; }
                    }

                    int k=-1;
                   
                    if ((v&1) > 0 && (visited[y*(N+1)+x]&1) == 0)
                    {
                        k=0;
                    }
                    else if ((v&2) > 0 && (visited[y*(N+1)+x]&2) == 0)
                    {
                        k=1;
                    }

                    if (k>=0)
                    {
                        int x0 = x + off0[k][0];
                        int y0 = y + off0[k][1];
                        int x1 = x + off1[k][0];
                        int y1 = y + off1[k][1];

                        try {
                            Brent.Function f = new Edge(data.lat + y0 * 
DEM.res, data.lon + x0 * DEM.res, 
                                                        data.lat + y1 * 
DEM.res, data.lon + x1 * DEM.res, 
                                                        level);
                            double f0 = elevation(x0, y0) - level;
                            double f1 = elevation(x1, y1) - level;
                            double delta;
                            if (Math.abs(f0) < epsilon)
                            {
                                delta = 0;
                            }
                            else if (Math.abs(f1) < epsilon)
                                continue;
                            else
                                delta = Brent.zero(f, 0, 1-epsilon);
                            
                            Position p = new Position(x, y, data.lon + 
(x0+delta*(x1-x0))*DEM.res, data.lat + (y0+delta*(y1-y0))*DEM.res, k);
                            p.markEdge();
                            isolines.add(traceByStepping(level, p));
                        }
                        catch (RuntimeException ex)
                        {
                            debug("error: %s", ex.toString());
                            ex.printStackTrace();
                            return;
                        }
                    }
                }
            }
        }

        private Isoline traceByStepping(double level, Position p)
        {
            debug("traceByStepping: starting contour %f %d %d %f %f %d", level, 
p.ix, p.iy, p.x, p.y, p.edge);
            int direction = 1;
            int n = 0;
            Position startP = new Position(p);
            boolean foundEnd = false;

            Isoline line = new Isoline(level);

            while (true)
            {
                debug("traceByStepping: %f %d %d %f %f %d", level, p.ix, p.iy, 
p.x, p.y, p.edge);
                visited[p.iy*(N+1)+p.ix] |= brd[p.edge];

                if (n>0 && p.ix == startP.ix && p.iy == startP.iy && 
quickDistance(p.x, p.y, startP.x, startP.y) < 5)
                {
                    debug("closed curve!");
                    line.close();
                    break;
                }
                else if (p.ix < minX || p.iy < minY || p.ix >= maxX || p.iy >= 
maxY)
                {
                    if (foundEnd) // did we already reach one end?
                    {
                        debug("second border reached!");
                        break;
                    }
                    else
                    {
                        debug("border reached!");
                        foundEnd = true;
                        n = 0;
                        direction *= -1;
                        p = new Position(startP);
                        p.moveCell();
                        continue;
                    }
                }
                n++;
                if (!line.addCell(p, direction) || line.points.size() > 
maxPoints)
                {
                    debug("ending contour");
                    isolines.add(line);
                    return line; 
                }
            }

            return line;
        }

    }

    public static double quickDistance(double long1, double lat1, double long2, 
double lat2) 
    {
        double latDiff;
        if (lat1 < lat2)
            latDiff = lat2 - lat1;
        else
            latDiff = lat1 - lat2;      
        if (latDiff > 90)
            latDiff -= 180;
        
        double longDiff;
        if (long1 < long2)
            longDiff = long2 - long1;
        else
            longDiff = long1 - long2;
        if (longDiff > 180)
            longDiff -= 360;
        
        // scale longDiff by cosine of average latitude
        longDiff *= Math.cos(Math.PI / 180 * Math.abs((lat1 + lat2) / 2));
        
        double distDegSq = (latDiff * latDiff) + (longDiff * longDiff);

        return 40075000 * Math.sqrt(distDegSq) / 360;
    }

    /**
     * Returns the orthodromic distance between two geographic coordinates in 
WGS84 datum.
     * The orthodromic distance is the shortest distance between two points
     * on a sphere's surface. The orthodromic path is always on a great circle.
     *
     * @param  x1 Longitude of first  point (in degrees).
     * @param  y1 Latitude  of first  point (in degrees).
     * @param  x2 Longitude of second point (in degrees).
     * @param  y2 Latitude  of second point (in degrees).
     * @return The orthodromic distance (in meters).
     *
     */
    public static double orthodromicDistance(double x1, double y1, double x2, 
double y2) 
    {
        x1 = Math.toRadians(x1);
        y1 = Math.toRadians(y1);
        x2 = Math.toRadians(x2);
        y2 = Math.toRadians(y2);
        final int MAX_ITERATIONS = 100;
        final double EPS = 5.0E-14;
        final double F = 1 / inverseFlattening;
        final double R = 1 - F;
        double tu1 = R * Math.sin(y1) / Math.cos(y1);
        double tu2 = R * Math.sin(y2) / Math.cos(y2);
        double cu1 = 1 / Math.sqrt(tu1 * tu1 + 1);
        double cu2 = 1 / Math.sqrt(tu2 * tu2 + 1);
        double su1 = cu1 * tu1;
        double s = cu1 * cu2;
        double baz = s * tu2;
        double faz = baz * tu1;
        double x = x2 - x1;
        for (int i = 0; i < MAX_ITERATIONS; i++) 
        {
            final double sx = Math.sin(x);
            final double cx = Math.cos(x);
            tu1 = cu2 * sx;
            tu2 = baz - su1 * cu2 * cx;
            final double sy = Math.sqrt(tu1*tu1 + tu2*tu2);;
            final double cy = s * cx + faz;
            final double y = Math.atan2(sy, cy);
            final double SA = s * sx / sy;
            final double c2a = 1 - SA * SA;
            double cz = faz + faz;
            if (c2a > 0) {
                cz = -cz / c2a + cy;
            }
            double e = cz * cz * 2 - 1;
            double c = ((-3 * c2a + 4) * F + 4) * c2a * F / 16;
            double d = x;
            x = ((e * cy * c + cz) * sy * c + y) * SA;
            x = (1 - c) * x * F + x2 - x1;
            if (Math.abs(d - x) <= EPS) {
                x = Math.sqrt((1 / (R * R) - 1) * c2a + 1) + 1;
                x = (x - 2) / x;
                c = 1 - x;
                c = (x * x / 4 + 1) / c;
                d = (0.375 * x * x - 1) * x;
                x = e * cy;
                s = 1 - 2 * e;
                s = ((((sy * sy * 4 - 3) * s * cz * d / 6 - x) * d / 4 + cz) * 
sy * d + y) * c * R * semiMajorAxis;
                return s;
            }
        }
        final double LEPS = 1.0E-10;
        if (Math.abs(x1 - x2) <= LEPS && Math.abs(y1 - y2) <= LEPS) {
            return 0;
        }
        if (Math.abs(y1) <= LEPS && Math.abs(y2) <= LEPS) {
            return Math.abs(x1 - x2) * semiMajorAxis;
        }
        throw new ArithmeticException("no convergence");
    }

    private static class DEMMapDataSource extends MapperBasedMapDataSource 
implements LoadableMapDataSource
    {
        LoadableMapDataSource parent;
        List<String> copyright = new ArrayList<String>();

        DEMMapDataSource(LoadableMapDataSource parent, EnhancedProperties props)
        {
            this.parent = parent;
            config(props);
        }

        public boolean isFileSupported(String name)
        {
            return false;
        }

        public void load(String name)
            throws FileNotFoundException, FormatException
        {
            throw new FormatException("load not supported");
        }

        public LevelInfo[] mapLevels()
        {
            return parent.mapLevels();
        }

        public String[] copyrightMessages()
        {
            return copyright.toArray(new String[1]);
        }
    }
}
/*
 * Copyright (C) 2009 Christian Gawron
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 * 
 * Author: Christian Gawron
 * Create date: 03-Jul-2009
 */
package uk.me.parabola.mkgmap.reader.dem;

/**
 * Find zero of a function using Brent's method.
 */
public class Brent
{
    
    public interface Function
    {
        public double eval(double x);
    }


    static double epsilon = 3.0e-10;

    public static double zero(Function f, double x1, double x2) 
    {
        return zero(f, x1, x2, 1e-8, 100);
    }

    public static double zero(Function f, double x1, double x2, double tol, int 
maxit)
    {
        double a=x1, b=x2, c=x2, d=0, e=0, min1, min2;
        double fa=f.eval(a), fb=f.eval(b), fc;
        double p, q, r, s, tolerance, xm;

        if ((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0))
            throw new ArithmeticException("Root must be bracketed");

        fc=fb;
        for (int iterations=0; iterations < maxit; iterations++) 
        {
            if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
                c=a;
                fc=fa;
                e=d=b-a;
            }
            if (Math.abs(fc) < Math.abs(fb)) {
                a=b;
                b=c;
                c=a;
                fa=fb;
                fb=fc;
                fc=fa;
            }
            tolerance=2.0*epsilon*Math.abs(b)+0.5*tol;
            xm=0.5*(c-b);
            if (Math.abs(xm) <= tolerance || fb == 0.0) return b;
            if (Math.abs(e) >= tolerance && Math.abs(fa) > Math.abs(fb)) {
                s=fb/fa;
                if (a == c) {
                    p=2.0*xm*s;
                    q=1.0-s;
                } else {
                    q=fa/fc;
                    r=fb/fc;
                    p=s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));
                    q=(q-1.0)*(r-1.0)*(s-1.0);
                }
                if (p > 0.0) q = -q;
                p=Math.abs(p);
                min1=3.0*xm*q-Math.abs(tolerance*q);
                min2=Math.abs(e*q);
                if (2.0*p < (min1 < min2 ? min1 : min2)) {
                    e=d;
                    d=p/q;
                } else {
                    d=xm;
                    e=d;
                }
            } else {
                d=xm;
                e=d;
            }
            a=b;
            fa=fb;
            if (Math.abs(d) > tolerance)
                b += d;
            else
                b += xm >= 0 ? tolerance : -tolerance;
            fb=f.eval(b);
        }
        throw new ArithmeticException("Maximum number of iterations exceeded");
    }
}
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to