Great, will try out you patch soon.<br><br>Is it compatible with srtm1&quot; data? - I would like to create maps from 1&quot; SRTM from <a href="http://viewfinderpanoramas.org">viewfinderpanoramas.org</a> <br><br>How big is the output size relative to other tools, have you compared it to other tools like groundtruth (to mp and then mkgmap, or using cgpsmapper)?<br>
SRTM2OSM mainly produced huge output files, much bigger than needed. groundtruth is much better in that respect.<br><br>What happens when running non void-filled data?<br><br><div class="gmail_quote">2009/7/3 Christian Gawron <span dir="ltr">&lt;<a href="mailto:christian.gawron@gmx.de">christian.gawron@gmx.de</a>&gt;</span><br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Dear all,<br>
<br>
the attached java classes together with the patch enables mkgmap to create contour lines directly from digital elevation model (DEM) data. This may be useful for those who don&#39;t want to ceate contour lines with other tools and store them in huge(!) files.<br>

<br>
It introduces the following new options:<br>
--contours   If set, create contours.<br>
--dem-type  Valid values are CGIAR (CGIAR geotiff), ASTER (ASTER geotiff) or SRTM (.hgt files). Default is CGIAR.<br>
--dem-path  Path to the DEM data files. The default is type-dependant.<br>
--dem-increment Verical distance between the contour lines (default is 10m).<br>
<br>
The algorithm used is a modified version of that described in &quot;An Adaptive Grid Contouring Algorithm&quot; by Downing and Zoraster (see <a href="http://mapcontext.com/autocarto/proceedings/auto-carto-5/pdf/an-adaptive-grid-contouring-algorithm.pdf" target="_blank">http://mapcontext.com/autocarto/proceedings/auto-carto-5/pdf/an-adaptive-grid-contouring-algorithm.pdf</a>) and uses bicubic interpolation of the DEM.<br>

<br>
Please feel free to send me improvements, bug reports etc.<br>
<br>
Best wishes<br><font color="#888888">
Christian<br>
<br>
<br>
</font><br>Index: src/uk/me/parabola/mkgmap/Option.java<br>
===================================================================<br>
--- src/uk/me/parabola/mkgmap/Option.java       (Revision 1077)<br>
+++ src/uk/me/parabola/mkgmap/Option.java       (Arbeitskopie)<br>
@@ -34,7 +34,7 @@<br>
                }<br>
        }<br>
<br>
-       protected Option(String option, String value) {<br>
+       public Option(String option, String value) {<br>
                this.option = option;<br>
                this.value = value;<br>
        }<br>
Index: src/uk/me/parabola/mkgmap/CommandArgsReader.java<br>
===================================================================<br>
--- src/uk/me/parabola/mkgmap/CommandArgsReader.java    (Revision 1077)<br>
+++ src/uk/me/parabola/mkgmap/CommandArgsReader.java    (Arbeitskopie)<br>
@@ -129,7 +129,7 @@<br>
         * @param option The option name.<br>
         * @param value Its value.<br>
         */<br>
-       private void addOption(String option, String value) {<br>
+       public void addOption(String option, String value) {<br>
                CommandOption opt = new CommandOption(option, value);<br>
                addOption(opt);<br>
        }<br>
@@ -181,13 +181,21 @@<br>
                return arglist.iterator();<br>
        }<br>
<br>
+        public ArgList getArgList() {<br>
+               return arglist;<br>
+       }<br>
+<br>
+        public EnhancedProperties getArgs() {<br>
+           return args;<br>
+       }<br>
+<br>
        /**<br>
         * Read a config file that contains more options.  When the number of<br>
         * options becomes large it is more convenient to place them in a file.<br>
         *<br>
         * @param filename The filename to obtain options from.<br>
         */<br>
-       private void readConfigFile(String filename) {<br>
+       public void readConfigFile(String filename) {<br>
                Options opts = new Options(new OptionProcessor() {<br>
                        public void processOption(Option opt) {<br>
                                log.debug(&quot;incoming opt&quot;, opt.getOption(), opt.getValue());<br>
@@ -206,14 +214,14 @@<br>
         * the argument to be processed in order.  Options can be intersperced with<br>
         * filenames.  The options take effect where they appear.<br>
         */<br>
-       interface ArgType {<br>
+       public interface ArgType {<br>
                public abstract void processArg();<br>
        }<br>
<br>
        /**<br>
         * A filename.<br>
         */<br>
-       class Filename implements ArgType {<br>
+       public class Filename implements ArgType {<br>
                private final String name;<br>
                private boolean useFilenameAsMapname = true;<br>
<br>
@@ -270,7 +278,7 @@<br>
        /**<br>
         * An option argument.  A key value pair.<br>
         */<br>
-       class CommandOption implements ArgType {<br>
+       public class CommandOption implements ArgType {<br>
                private final Option option;<br>
<br>
                private CommandOption(Option option) {<br>
@@ -297,7 +305,7 @@<br>
        /**<br>
         * The arguments are held in this list.<br>
         */<br>
-       class ArgList implements Iterable&lt;CommandArgsReader.ArgType&gt; {<br>
+       public class ArgList implements Iterable&lt;CommandArgsReader.ArgType&gt; {<br>
                private final List&lt;ArgType&gt; alist;<br>
<br>
                private int filenameCount;<br>
Index: src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java<br>
===================================================================<br>
--- src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java      (Revision 1077)<br>
+++ src/uk/me/parabola/mkgmap/reader/MapperBasedMapDataSource.java      (Arbeitskopie)<br>
@@ -33,6 +33,7 @@<br>
 import uk.me.parabola.mkgmap.general.MapPoint;<br>
 import uk.me.parabola.mkgmap.general.MapShape;<br>
 import uk.me.parabola.mkgmap.general.RoadNetwork;<br>
+import uk.me.parabola.mkgmap.reader.dem.DEM;<br>
 import uk.me.parabola.util.Configurable;<br>
 import uk.me.parabola.util.EnhancedProperties;<br>
<br>
@@ -146,5 +147,8 @@<br>
                        // the overview section.<br>
                        mapper.addShape(background);<br>
                }<br>
+               if (getConfig().getProperty(&quot;contours&quot;, false)) {<br>
+                   DEM.createContours(mapper, getConfig());<br>
+               }<br>
        }<br>
 }<br>
<br>/*<br>
 * Copyright (C) 2009 Christian Gawron<br>
 *<br>
 *  This program is free software; you can redistribute it and/or modify<br>
 *  it under the terms of the GNU General Public License version 2 as<br>
 *  published by the Free Software Foundation.<br>
 *<br>
 *  This program is distributed in the hope that it will be useful,<br>
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
 *  GNU General Public License for more details.<br>
 *<br>
 *<br>
 * Author: Christian Gawron<br>
 * Create date: 03-Jul-2009<br>
 */<br>
package uk.me.parabola.mkgmap.reader.dem;<br>
<br>
import java.io.*;<br>
import java.nio.channels.FileChannel;<br>
import java.nio.MappedByteBuffer;<br>
<br>
import java.util.Vector;<br>
import java.util.Locale;<br>
<br>
import com.sun.media.jai.codec.*;<br>
import javax.media.jai.*;<br>
import java.awt.image.renderable.ParameterBlock;<br>
import java.awt.image.Raster;<br>
import java.awt.image.DataBuffer;<br>
<br>
import uk.me.parabola.imgfmt.Utils;<br>
import uk.me.parabola.imgfmt.app.Area;<br>
import uk.me.parabola.imgfmt.app.Coord;<br>
import uk.me.parabola.mkgmap.general.MapDetails;<br>
import uk.me.parabola.mkgmap.general.MapPoint;<br>
import uk.me.parabola.mkgmap.general.MapLine;<br>
import uk.me.parabola.util.EnhancedProperties;<br>
<br>
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;<br>
<br>
/**<br>
 * Create contour lines using an algorithm similar to that described in<br>
 * &lt;a href=&quot;<a href="http://mapcontext.com/autocarto/proceedings/auto-carto-5/pdf/an-adaptive-grid-contouring-algorithm.pdf" target="_blank">http://mapcontext.com/autocarto/proceedings/auto-carto-5/pdf/an-adaptive-grid-contouring-algorithm.pdf</a>&quot;&gt;An Adaptive Grid Contouring Algorithm&lt;/a&gt; by Downing and Zoraster.<br>

 */<br>
public abstract class DEM<br>
{<br>
    final static double epsilon = 1e-9;<br>
    final static double delta = 1.5;<br>
    final static int maxPoints=200000;<br>
    final static double minDist = 15;<br>
    final static double maxDist = 21;<br>
    final static double step = 0.01;<br>
    final static double semiMajorAxis = 6378137.0;<br>
    final static double inverseFlattening = 298.257223563;<br>
<br>
    static int M=1200;<br>
    static int N=M;<br>
    static double res = 1.0/N;<br>
<br>
    short values[] = null;<br>
    int lat;<br>
    int lon;<br>
<br>
    abstract double ele(int x, int y);<br>
    abstract void read(int minLon, int minLat, int maxLon, int maxLat);<br>
    abstract void serializeCopyRight(Writer out) throws IOException;<br>
<br>
    public static void createContours(MapDetails mapper, EnhancedProperties config)<br>
    {<br>
        Area bounds = mapper.getBounds();<br>
<br>
        double minLat = Utils.toDegrees(bounds.getMinLat());<br>
        double minLon = Utils.toDegrees(bounds.getMinLong());<br>
        double maxLat = Utils.toDegrees(bounds.getMaxLat());<br>
        double maxLon = Utils.toDegrees(bounds.getMaxLong());<br>
<br>
        System.out.printf(&quot;bounds: %f %f %f %f\n&quot;, minLat, minLon, maxLat, maxLon);<br>
        DEM data;<br>
        String demType = config.getProperty(&quot;dem-type&quot;, &quot;CGIAR&quot;);<br>
<br>
        String dataPath;<br>
        if (demType.equals(&quot;ASTER&quot;)) {<br>
            dataPath = config.getProperty(&quot;dem-path&quot;, &quot;ASTER&quot;);<br>
            data = new ASTERGeoTiff(dataPath, minLat, minLon, maxLat, maxLon);<br>
        }<br>
        else if (demType.equals(&quot;CGIAR&quot;)) {<br>
            dataPath = config.getProperty(&quot;dem-path&quot;, &quot;CGIAR&quot;);<br>
            data = new CGIARGeoTiff(dataPath, minLat, minLon, maxLat, maxLon);<br>
        }<br>
        else {<br>
            dataPath = config.getProperty(&quot;dem-path&quot;, &quot;SRTM&quot;);<br>
            data = new HGTDEM(dataPath, minLat, minLon, maxLat, maxLon);<br>
        }<br>
<br>
        Isolines lines = data.new Isolines(data, minLat, minLon, maxLat, maxLon);<br>
        int increment = config.getProperty(&quot;dem-increment&quot;, 10);<br>
<br>
        lines.addLevels(0, increment);<br>
<br>
        for (Isolines.Isoline line : lines.isolines) {<br>
            MapLine contour = new MapLine();<br>
            if ((line.level % 200) == 0) {<br>
                contour.setType(0x22);     // major contour<br>
                contour.setMinResolution(18);<br>
            }<br>
            else if ((line.level % 50) == 0) {<br>
                contour.setType(0x21);     // major contour<br>
                contour.setMinResolution(20);<br>
            }<br>
            else {<br>
                contour.setType(0x20);     // major contour<br>
                contour.setMinResolution(22);<br>
            }<br>
<br>
            contour.setName(String.format(&quot;%.2f&quot;, line.level * 3.2808399));<br>
            contour.setPoints(line.points);<br>
            mapper.addLine(contour);<br>
        }<br>
<br>
    }<br>
<br>
    public static class HGTDEM extends DEM<br>
    {<br>
        MappedByteBuffer buffer = null;<br>
<br>
        public HGTDEM(String dataPath, double minLat, double minLon, double maxLat, double maxLon)<br>
        {<br>
            this.lat = (int) minLat;<br>
            this.lon = (int) minLon;<br>
            if (maxLat &gt; lat+1 || maxLon &gt; lon+1)<br>
                throw new RuntimeException(&quot;Area too large (must not span more than one SRTM file)&quot;);<br>
<br>
            String northSouth = lat &lt; 0 ? &quot;S&quot; : &quot;N&quot;;<br>
            String eastWest = lon &gt; 0 ? &quot;E&quot; : &quot;W&quot;;<br>
            String fileName = String.format(&quot;%s/%s%02d%s%03d.hgt&quot;, dataPath,<br>
                                            northSouth, lat &lt; 0 ? -lat : lat,<br>
                                            eastWest, lon &lt; 0 ? -lon : lon);<br>
            try {<br>
                FileInputStream is = new FileInputStream(fileName);<br>
                buffer = is.getChannel().map(READ_ONLY, 0, 2*(M+1)*(M+1));<br>
            }<br>
            catch (Exception e) {<br>
                throw new RuntimeException(e);<br>
            }<br>
        }<br>
<br>
        public  void read(int minLon, int minLat, int maxLon, int maxLat)<br>
        {<br>
        }<br>
<br>
        public double ele(int x, int y)<br>
        {<br>
            return buffer.getShort(2*((M-y)*(M+1)+x))+delta;<br>
        }<br>
<br>
        public void serializeCopyRight(Writer out) throws IOException<br>
        {<br>
            out.write(&quot;  &lt;copyright&gt;\n&quot;);<br>
            out.write(&quot;  Contour lines generated from DEM data by NASA\n&quot;);<br>
            out.write(&quot;  &lt;/copyright&gt;\n&quot;);<br>
        }<br>
    }<br>
<br>
    public static class CGIARGeoTiff extends DEM<br>
    {<br>
        Raster raster;<br>
        String fileName;<br>
        int minLat, minLon, maxLat, maxLon;<br>
        PlanarImage image;<br>
<br>
        public CGIARGeoTiff(String dataPath, double minLat, double minLon, double maxLat, double maxLon)<br>
        {<br>
            this.lat = ((int) (minLat/5))*5;<br>
            this.lon = ((int) (minLon/5))*5;<br>
            if (maxLat &gt; lat+5 || maxLon &gt; lon+5)<br>
                throw new RuntimeException(&quot;Area too large (must not span more than one CGIAR GeoTIFF)&quot;);<br>
<br>
            int tileX, tileY;<br>
            tileX = (180 + lon)/5 + 1;<br>
            tileY = (60 - lat)/5;<br>
            this.fileName = String.format(&quot;%s/srtm_%02d_%02d.tif&quot;, dataPath, tileX, tileY);<br>
            init();<br>
        }<br>
<br>
        public void serializeCopyRight(Writer out) throws IOException<br>
        {<br>
            out.write(&quot;  &lt;copyright&gt;\n&quot;);<br>
            out.write(&quot;  Contour lines generated from improved SRTM data by CIAT-CSI (see <a href="http://srtm.csi.cgiar.org" target="_blank">http://srtm.csi.cgiar.org</a>)\n&quot;);<br>
            out.write(&quot;  &lt;/copyright&gt;\n&quot;);<br>
        }<br>
<br>
        public  void read(int minLon, int minLat, int maxLon, int maxLat)<br>
        {<br>
            this.minLon = minLon;<br>
            this.minLat = minLat;<br>
            this.maxLon = maxLon;<br>
            this.maxLat = maxLat;<br>
            raster = image.getData(new java.awt.Rectangle(minLon, 6000-maxLat-1, maxLon-minLon+1, maxLat-minLat+1));<br>
            System.out.printf(&quot;read: %d %d %d %d\n&quot;, minLon, 6000-maxLat-1, maxLon-minLon+1, maxLat-minLat+1);<br>
        }<br>
<br>
        void init()<br>
        {<br>
            System.out.printf(&quot;CGIAR GeoTIFF: %s\n&quot;, fileName);<br>
            N = 6000;<br>
            M = 6000;<br>
            res = 5.0/M;<br>
<br>
            try {<br>
                SeekableStream s = new FileSeekableStream(fileName);<br>
                ParameterBlock pb = new ParameterBlock();<br>
                pb.add(s);<br>
<br>
                TIFFDecodeParam param = new TIFFDecodeParam();<br>
                pb.add(param);<br>
<br>
                RenderedOp op = JAI.create(&quot;tiff&quot;, pb);<br>
                image = op.createInstance();<br>
                System.out.printf(&quot;Image: %d %d %d %d\n&quot;, image.getWidth(), image.getHeight(),<br>
                                  image.getNumXTiles(), image.getNumYTiles());<br>
            }<br>
            catch (Exception e) {<br>
                throw new RuntimeException(e);<br>
            }<br>
        }<br>
<br>
        public double ele(int x, int y)<br>
        {<br>
            try {<br>
                int elevation = raster.getPixel(x, 6000-y-1, (int[])null)[0];<br>
                return elevation+delta;<br>
            }<br>
            catch (ArrayIndexOutOfBoundsException ex) {<br>
                System.out.printf(&quot;ele: (%d, %d) (%d, %d, %d, %d)  %s\n&quot;,<br>
                                  x, 6000-y-1,<br>
                                  raster.getMinX(), raster.getMinY(),<br>
                                  raster.getWidth(), raster.getHeight(), ex.toString());<br>
                throw ex;<br>
            }<br>
        }<br>
    }<br>
<br>
    public static class ASTERGeoTiff extends DEM<br>
    {<br>
        Raster raster;<br>
        String fileName;<br>
        int minLat, minLon, maxLat, maxLon;<br>
        PlanarImage image;<br>
<br>
        public ASTERGeoTiff(String dataPath, double minLat, double minLon, double maxLat, double maxLon)<br>
        {<br>
            this.lat = (int) minLat;<br>
            this.lon = (int) minLon;<br>
            if (maxLat &gt; lat+1 || maxLon &gt; lon+1)<br>
                throw new RuntimeException(&quot;Area too large (must not span more than one ASTER GeoTIFF)&quot;);<br>
<br>
            String northSouth = lat &lt; 0 ? &quot;S&quot; : &quot;N&quot;;<br>
            String eastWest = lon &gt; 0 ? &quot;E&quot; : &quot;W&quot;;<br>
            fileName = String.format(&quot;%s/ASTGTM_%s%02d%s%03d_dem.tif&quot;, dataPath,<br>
                                     northSouth, lat &lt; 0 ? -lat : lat,<br>
                                     eastWest, lon &lt; 0 ? -lon : lon);<br>
            init();<br>
        }<br>
<br>
        public void serializeCopyRight(Writer out) throws IOException<br>
        {<br>
            out.write(&quot;  &lt;copyright&gt;\n&quot;);<br>
            out.write(&quot;  Contour lines generated from DGM data by ASTER (see <a href="https://wist.echo.nasa.gov/%7Ewist/api/imswelcome" target="_blank">https://wist.echo.nasa.gov/~wist/api/imswelcome</a>)\n&quot;);<br>

            out.write(&quot;  &lt;/copyright&gt;\n&quot;);<br>
        }<br>
<br>
        public  void read(int minLon, int minLat, int maxLon, int maxLat)<br>
        {<br>
            this.minLon = minLon;<br>
            this.minLat = minLat;<br>
            this.maxLon = maxLon;<br>
            this.maxLat = maxLat;<br>
            raster = image.getData(new java.awt.Rectangle(minLon, 3601-maxLat-1, maxLon-minLon+1, maxLat-minLat+1));<br>
            System.out.printf(&quot;read: %d %d %d %d\n&quot;, minLon, 3601-maxLat-1, maxLon-minLon+1, maxLat-minLat+1);<br>
        }<br>
<br>
        void init()<br>
        {<br>
            System.out.printf(&quot;ASTER GeoTIFF: %s\n&quot;, fileName);<br>
            N = 3600;<br>
            M = 3600;<br>
            res = 1.0/M;<br>
<br>
            try {<br>
                SeekableStream s = new FileSeekableStream(fileName);<br>
                ParameterBlock pb = new ParameterBlock();<br>
                pb.add(s);<br>
<br>
                TIFFDecodeParam param = new TIFFDecodeParam();<br>
                pb.add(param);<br>
<br>
                RenderedOp op = JAI.create(&quot;tiff&quot;, pb);<br>
                image = op.createInstance();<br>
                System.out.printf(&quot;Image: %d %d %d %d\n&quot;, image.getWidth(), image.getHeight(),<br>
                                  image.getNumXTiles(), image.getNumYTiles());<br>
            }<br>
            catch (Exception e) {<br>
                throw new RuntimeException(e);<br>
            }<br>
        }<br>
<br>
        public double ele(int x, int y)<br>
        {<br>
            try {<br>
                int elevation = raster.getPixel(x, 3601-y-1, (int[])null)[0];<br>
                return elevation+delta;<br>
            }<br>
            catch (ArrayIndexOutOfBoundsException ex) {<br>
                System.out.printf(&quot;ele: (%d, %d) (%d, %d, %d, %d)  %s\n&quot;,<br>
                                  x, 3601-y-1,<br>
                                  raster.getMinX(), raster.getMinY(),<br>
                                  raster.getWidth(), raster.getHeight(), ex.toString());<br>
                throw ex;<br>
            }<br>
        }<br>
    }<br>
<br>
    private static void debug(String format, Object ... args)<br>
    {<br>
        //System.out.printf(format + &quot;\n&quot;, args);<br>
    }<br>
<br>
<br>
    int lastXi = -1;<br>
    int lastYi = -1;<br>
<br>
    final static int bcInv[][]= {<br>
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},<br>
        { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},<br>
        { -3, 0, 0, 3, 0, 0, 0, 0, -2, 0, 0, -1, 0, 0, 0, 0},<br>
        { 2, 0, 0, -2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},<br>
        { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},<br>
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},<br>
        { 0, 0, 0, 0, -3, 0, 0, 3, 0, 0, 0, 0, -2, 0, 0, -1},<br>
        { 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0, 0, 1, 0, 0, 1},<br>
        { -3, 3, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},<br>
        { 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0},<br>
        { 9, -9, 9, -9, 6, 3, -3, -6, 6, -6, -3, 3, 4, 2, 1, 2},<br>
        { -6, 6, -6, 6, -4, -2, 2, 4, -3, 3, 3, -3, -2, -1, -1, -2},<br>
        { 2, -2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},<br>
        { 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0},<br>
        { -6, 6, -6, 6, -3, -3, 3, 3, -4, 4, 2, -2, -2, -2, -1, -1},<br>
        { 4, -4, 4, -4, 2, 2, -2, -2, 2, -2, -2, 2, 1, 1, 1, 1}<br>
    };<br>
<br>
    static int lastId=1000000000;<br>
    static double lastX = 0;<br>
    static double lastY = 0;<br>
<br>
    static int edge[] = new int[2];<br>
<br>
    static int off0[][] = {{ 0,  0},<br>
                           { 0,  0},<br>
                           { 0,  1},<br>
                           { 1,  1}};<br>
<br>
    static int off1[][] = {{ 1,  0},<br>
                           { 0,  1},<br>
                           { 1,  1},<br>
                           { 1,  0}};<br>
<br>
    static int brd[] = { 1, 2, 4, 8 };<br>
    static int inv[] = { 4, 8, 1, 2 };<br>
<br>
    static int rev[] = { 2, 3, 0, 1 };<br>
<br>
    static int mov[][] = {{ 0, -1},<br>
                          {-1,  0},<br>
                          { 0,  1},<br>
                          { 1,  0}};<br>
<br>
<br>
    double bc[][] = new double[4][4];<br>
    double bc_y[] = new double[4];<br>
    double bc_y1[] = new double[4];<br>
    double bc_y2[] = new double[4];<br>
    double bc_y12[] = new double[4];<br>
    double bc_Coeff[] = new double[16];<br>
    double bc_x[] = new double[16];<br>
<br>
    private void recalculateCoefficients(int xi, int yi)<br>
    {<br>
        double v00, vp0, v0p, vpp;<br>
        double vm0, v0m, vpm, vmp, vmm, vmP, vPm;<br>
        double vP0, v0P, vPp, vpP, vPP;<br>
<br>
        v00 = ele(xi, yi);<br>
        v0p = ele(xi, yi+1);<br>
        vpp = ele(xi+1, yi+1);<br>
        vp0 = ele(xi+1, yi);<br>
<br>
        vm0 = ele(xi-1, yi);<br>
        v0m = ele(xi, yi-1);<br>
        vmp = ele(xi-1, yi+1);<br>
        vpm = ele(xi+1, yi-1);<br>
        vmm = ele(xi-1, yi-1);<br>
        vmP = ele(xi+2, yi-1);<br>
        vPm = ele(xi-1, yi+2);<br>
<br>
        vP0 = ele(xi+2, yi);<br>
        v0P = ele(xi, yi+2);<br>
        vPp = ele(xi+2, yi+1);<br>
        vpP = ele(xi+1, yi+2);<br>
        vPP = ele(xi+2, yi+2);<br>
<br>
        bc_y[0] = v00;<br>
        bc_y[1] = vp0;<br>
        bc_y[2] = vpp;<br>
        bc_y[3] = v0p;<br>
<br>
        bc_y1[0] = (vp0-vm0)/2;<br>
        bc_y1[1] = (vP0-v00)/2;<br>
        bc_y1[2] = (vPp-v0p)/2;<br>
        bc_y1[3] = (vpp-vmp)/2;<br>
<br>
        bc_y2[0] = (v0p-v0m)/2;<br>
        bc_y2[1] = (vpp-vpm)/2;<br>
        bc_y2[2] = (vpP-vp0)/2;<br>
        bc_y2[3] = (v0P-v00)/2;<br>
<br>
        bc_y12[0] = (vpp - vpm - vmp + vmm) / 4;<br>
        bc_y12[0] = (vPp - vPm - v0p + v0m) / 4;<br>
        bc_y12[2] = (vPP - vP0 - v0P + v00) / 4;<br>
        bc_y12[0] = (vpP - vp0 - vmP + vm0) / 4;<br>
<br>
        int j, i;<br>
        double s;<br>
<br>
        for (i=0; i&lt;4; i++)<br>
        {<br>
            bc_x[i]=bc_y[i];<br>
            bc_x[i+4]=bc_y1[i];<br>
            bc_x[i+8]=bc_y2[i];<br>
            bc_x[i+12]=bc_y12[i];<br>
        }<br>
<br>
        for (i=0; i&lt;16; i++)<br>
        {<br>
            s = 0;<br>
            for (int k=0; k&lt;16; k++) s += bcInv[i][k]*bc_x[k];<br>
            bc_Coeff[i] = s;<br>
        }<br>
<br>
        int l = 0;<br>
        for (i=0; i&lt;4; i++)<br>
            for (j=0; j&lt;4; j++)<br>
                bc[i][j] = bc_Coeff[l++];<br>
    }<br>
<br>
    public double gradient(double lat, double lon, double[] grad)<br>
    {<br>
        grad[0] = 0;<br>
        grad[1] = 0;<br>
<br>
        double x = (lon-this.lon)/res;<br>
        double y = (lat-this.lat)/res;<br>
<br>
        int xi = (int) x;<br>
        int yi = (int) y;<br>
<br>
        if (lastXi != xi || lastYi != yi)<br>
        {<br>
            debug(&quot;new Cell for interpolation: %d %d&quot;, xi, yi);<br>
            recalculateCoefficients(xi, yi);<br>
            lastXi = xi;<br>
            lastYi = yi;<br>
        }<br>
<br>
        double t = x - xi;<br>
        double u = y - yi;<br>
<br>
        if  (xi &lt; 0 || xi &gt; N+1 || yi &lt; 0 || yi &gt; N+1)<br>
            throw new IndexOutOfBoundsException(String.format(&quot;(%f, %f)-&gt;(%d, %d)&quot;, lat, lon, xi, yi));<br>
<br>
        double val = 0;<br>
        for (int i=3; i&gt;=0; i--)<br>
        {<br>
            val = t*val + ((bc[i][3]*u + bc[i][2])*u + bc[i][1])*u + bc[i][0];<br>
            grad[0] = u*grad[0] + (3*bc[3][i]*t + 2*bc[2][i])*t + bc[1][i];<br>
            grad[1] = t*grad[1] + (3*bc[i][3]*t + 2*bc[i][2])*t + bc[i][1];<br>
        }<br>
<br>
        return val;<br>
    }<br>
<br>
    public double elevation(double lat, double lon)<br>
    {<br>
        double x = (lon-this.lon)/res;<br>
        double y = (lat-this.lat)/res;<br>
<br>
        int xi = (int) x;<br>
        int yi = (int) y;<br>
<br>
        if (lastXi != xi || lastYi != yi)<br>
        {<br>
            debug(&quot;new Cell for interpolation: %d %d&quot;, xi, yi);<br>
            recalculateCoefficients(xi, yi);<br>
            lastXi = xi;<br>
            lastYi = yi;<br>
        }<br>
<br>
        double t = x - xi;<br>
        double u = y - yi;<br>
<br>
        if  (xi &lt; 0 || xi &gt; N+1 || yi &lt; 0 || yi &gt; N+1)<br>
            throw new IndexOutOfBoundsException(String.format(&quot;(%f, %f)-&gt;(%d, %d)&quot;, lat, lon, xi, yi));<br>
<br>
        double val = 0;<br>
        for (int i=3; i&gt;=0; i--)<br>
        {<br>
            val = t*val + ((bc[i][3]*u + bc[i][2])*u + bc[i][1])*u + bc[i][0];<br>
        }<br>
<br>
        return val;<br>
    }<br>
<br>
    public double elevation(int x, int y)<br>
    {<br>
        if (x &lt; 0 || x &gt; N || y &lt; 0 || y &gt; N)<br>
            throw new IndexOutOfBoundsException(String.format(&quot;elevation: %d %d&quot;, x, y));<br>
        return ele(x, y);<br>
    }<br>
<br>
<br>
    class Isolines<br>
    {<br>
        DEM data;<br>
        int minX;<br>
        int maxX;<br>
        int minY;<br>
        int maxY;<br>
<br>
        double min;<br>
        double max;<br>
<br>
        Vector&lt;Isoline&gt; isolines = new Vector&lt;Isoline&gt;();<br>
<br>
        class Isoline<br>
        {<br>
            int id;<br>
            Vector&lt;Coord&gt; points;<br>
            double level;<br>
            boolean isClosed;<br>
<br>
            private Isoline(double level)<br>
            {<br>
                this.level = level;<br>
                isClosed = false;<br>
                id = lastId++;<br>
                points = new Vector&lt;Coord&gt;();<br>
            }<br>
<br>
            private class Edge implements Brent.Function<br>
            {<br>
                double x0, y0, x1, y1;<br>
                Edge(double x0, double y0, double x1, double y1)<br>
                {<br>
                    this.x0 = x0;<br>
                    this.y0 = y0;<br>
                    this.x1 = x1;<br>
                    this.y1 = y1;<br>
                }<br>
<br>
                public double eval(double d)<br>
                {<br>
                    double f = data.elevation(x0 + d * (x1-x0), y0 + d * (y1-y0)) - level;<br>
                    //System.out.printf(&quot;evalEdge: %f %f\n&quot;, d, f);<br>
                    return f;<br>
                }<br>
            }<br>
<br>
            private class FN implements Brent.Function<br>
            {<br>
                double x0, y0;<br>
                double dx, dy;<br>
<br>
                public void setParameter(double x0, double y0, double dx, double dy)<br>
                {<br>
                    this.x0 = x0;<br>
                    this.y0 = y0;<br>
                    this.dx = dx;<br>
                    this.dy = dy;<br>
                }<br>
<br>
                public double eval(double t)<br>
                {<br>
                    double f = data.elevation(y0+t*dy, x0+t*dx) - level;<br>
                    return f;<br>
                }<br>
            }<br>
<br>
            private FN fn = new FN();<br>
<br>
            double grad[] = new double[2];<br>
            double px[] = new double[4];<br>
            double py[] = new double[4];<br>
            int edges[] = new int[4];<br>
<br>
            boolean addCell(Position p, int direction)<br>
            {<br>
                debug(&quot;addCell: %f %d %d %d %d&quot;, level, p.ix, p.iy, p.edge, direction);<br>
<br>
                int c = 0;<br>
                for (int k=0; k&lt;4; k++)<br>
                {<br>
                    if (k == p.edge)<br>
                        continue;<br>
<br>
                    int x0 = p.ix + off0[k][0];<br>
                    int y0 = p.iy + off0[k][1];<br>
                    int x1 = p.ix + off1[k][0];<br>
                    int y1 = p.iy + off1[k][1];<br>
<br>
                    double l0 = elevation(x0, y0) - level;<br>
                    double l1 = elevation(x1, y1) - level;<br>
<br>
                    if (Math.abs(l1) &lt; epsilon || l0*l1 &lt; 0)<br>
                    {<br>
                        edges[c] = k;<br>
<br>
                        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);<br>
                        double f0 = elevation(x0, y0) - level;<br>
                        double f1 = elevation(x1, y1) - level;<br>
                        double delta;<br>
<br>
                        if (Math.abs(1) &lt; epsilon)<br>
                        {<br>
                            delta = 1;<br>
                        }<br>
                        else if (Math.abs(f0) &lt; epsilon)<br>
                            throw new RuntimeException(&quot;implementation error!&quot;);<br>
                        else<br>
                            delta = Brent.zero(f, epsilon, 1-epsilon);<br>
<br>
                        px[c] = data.lon + (x0+delta*(x1-x0))*DEM.res;<br>
                        py[c] = data.lat + (y0+delta*(y1-y0))*DEM.res;<br>
                        c++;<br>
                    }<br>
                }<br>
<br>
<br>
                if (c == 1)<br>
                {<br>
                    p.edge = edges[0];<br>
<br>
                    double px0 = p.x;<br>
                    double py0 = p.y;<br>
                    p.x = px[0];<br>
                    p.y = py[0];<br>
                    double px1 = p.x;<br>
                    double py1 = p.y;<br>
<br>
                    double xMin = data.lon + p.ix * DEM.res;<br>
                    double xMax = xMin + DEM.res;<br>
                    double yMin = data.lat + p.iy * DEM.res;<br>
                    double yMax = yMin + DEM.res;<br>
<br>
                    refineAdaptively(xMin, yMin, xMax, yMax, px0, py0, px1, py1, direction, maxDist);<br>
<br>
                    addPoint(p.x, p.y, direction);<br>
                    p.moveCell();<br>
                    return true;<br>
                }<br>
                else<br>
                {<br>
                    debug(&quot;addCellByStepping: %d&quot;, c);<br>
                    return addCellByStepping(p, direction, c, edges, px, py);<br>
                }<br>
            }<br>
<br>
            private void refineAdaptively(double xMin, double yMin, double xMax, double yMax,<br>
                                          double x0, double y0, double x1, double y1,<br>
                                          int direction, double maxDist)<br>
            {<br>
                double dist = orthodromicDistance(x0, y0, x1, y1);<br>
                if (dist &gt; maxDist)<br>
                {<br>
                    double dx = x1-x0;<br>
                    double dy = y1-y0;<br>
                    double xm, ym, f0, f1, t0, t1, t;<br>
                    Brent.Function f;<br>
<br>
                    xm = x0 + 0.5*dx;<br>
                    ym = y0 + 0.5*dy;<br>
                    double n = Math.sqrt(dx*dx+dy*dy);<br>
                    fn.setParameter(xm, ym, -dy/n, dx/n);<br>
                    f = fn;<br>
                    t0 = -0.05*res;<br>
                    t1 = 0.05*res;<br>
                    f0 = f.eval(t0);<br>
                    f1 = f.eval(t1);<br>
<br>
                    int count = 0;<br>
                    while (f0 * f1 &gt; 0 &amp;&amp; count++ &lt; 20) {<br>
                        if ((count &amp; 1) &gt; 0)<br>
                            t0 -= 0.05*res;<br>
                        else<br>
                            t1 += 0.05*res;<br>
                        f0 = f.eval(t0);<br>
                        f1 = f.eval(t1);<br>
                        debug(&quot;refine: %f %f %f %f&quot;, t0, t1, f0, f1);<br>
                    }<br>
<br>
                    if (f0 * f1 &lt; 0)<br>
                    {<br>
                        t = Brent.zero(f, t0, t1);<br>
                        xm -= t*dy;<br>
                        ym += t*dx;<br>
                    }<br>
                    else<br>
                    {<br>
                        debug(&quot;refine failed: %f %f %f %f&quot;, t0, t1, f0, f1);<br>
                        if (false) throw new RuntimeException(String.format(&quot;refine failed: %f %f %f %f %f %f %f %f&quot;, xMin, yMin, xMax, yMax, x0, y0, x1, y1));<br>
                        return;<br>
                    }<br>
<br>
                    if (xm &gt; xMin &amp;&amp; xm &lt; xMax &amp;&amp; ym &gt; yMin &amp;&amp; ym &lt; yMax)<br>
                        refineAdaptively(xMin, yMin, xMax, yMax, x0, y0, xm, ym, direction, maxDist*1.1);<br>
                    addPoint(xm, ym, direction);<br>
                    if (xm &gt; xMin &amp;&amp; xm &lt; xMax &amp;&amp; ym &gt; yMin &amp;&amp; ym &lt; yMax)<br>
                        refineAdaptively(xMin, yMin, xMax, yMax, xm, ym, x1, y1, direction, maxDist*1.1);<br>
                }<br>
            }<br>
<br>
            boolean addCellByStepping(Position p, int direction, int numEdges, int[] edges, double[] px, double[] py)<br>
            {<br>
                debug(&quot;addCellByStepping: %f %d %d %d %d&quot;, level, p.ix, p.iy, p.edge, direction);<br>
<br>
<br>
                double xMin = data.lon + p.ix * DEM.res;<br>
                double xMax = xMin + DEM.res;<br>
                double yMin = data.lat + p.iy * DEM.res;<br>
                double yMax = yMin + DEM.res;<br>
<br>
                double dt, t0, t1;<br>
                double f0, f1;<br>
                boolean edgeHit = false;<br>
                double h[] = new double[4];<br>
<br>
                int dir;<br>
                double n2 = Math.sqrt(1.0/(grad[0]*grad[0] + grad[1]*grad[1]));<br>
                double dx;<br>
                double dy;<br>
<br>
                int count=0;<br>
                int iMin = -1;<br>
                double md = 5000;<br>
<br>
                for (int i=0; i&lt;numEdges; i++) {<br>
                    gradient(p.y, p.x, grad);<br>
                    double dist = orthodromicDistance(p.x, p.y, px[i], py[i]);<br>
                    //double dist = (px[i]-p.x)*(px[i]-p.x)*grad[0]*grad[0] + (py[i]-p.y)*(py[i]-p.y)*grad[1]*grad[1];<br>
                    debug(&quot;distance %d: %f&quot;, i, dist);<br>
<br>
                    if (dist &lt; md &amp;&amp; (visited[p.iy*(N+1)+p.ix] &amp; brd[edges[i]]) == 0) {<br>
                        md = dist;<br>
                        iMin = i;<br>
                    }<br>
                }<br>
<br>
                p.edge = edges[iMin];<br>
<br>
                double px0 = p.x;<br>
                double py0 = p.y;<br>
                p.x = px[iMin];<br>
                p.y = py[iMin];<br>
                double px1 = p.x;<br>
                double py1 = p.y;<br>
<br>
                xMin = data.lon + p.ix * DEM.res;<br>
                xMax = xMin + DEM.res;<br>
                yMin = data.lat + p.iy * DEM.res;<br>
                yMax = yMin + DEM.res;<br>
<br>
                refineAdaptively(xMin, yMin, xMax, yMax, px0, py0, px1, py1, direction, maxDist);<br>
<br>
                addPoint(p.x, p.y, direction);<br>
                p.moveCell();<br>
                return true;<br>
            }<br>
<br>
            private void addPoint(double x, double y, int direction)<br>
            {<br>
                double dist = orthodromicDistance(x, y, lastX, lastY);<br>
                debug(&quot;addPoint: %f %f %f&quot;, x, y, dist);<br>
<br>
                if (dist &gt; minDist)<br>
                {<br>
                    if (direction &gt; 0)<br>
                        points.add(0, new Coord(y, x));<br>
                    else<br>
                        points.add(points.size(), new Coord(y, x));<br>
                    lastX = x;<br>
                    lastY = y;<br>
                }<br>
            }<br>
<br>
            private void close()<br>
            {<br>
                points.add(points.size(), points.get(0));<br>
                isClosed = true;<br>
            }<br>
<br>
            private void addMove(int x0, int y0, int x1, int y1, int direction)<br>
            {<br>
                double x;<br>
                double y;<br>
<br>
                if (x0 &lt; minX || x0 &gt;= maxX || y0 &lt; minY || y0 &gt;= maxY)<br>
                    return;<br>
                double l0 = data.elevation(x0, y0);<br>
                double l1 = data.elevation(x1, y1);<br>
                debug(&quot;addMove: %d %d %d %d %.2f %.2f %d&quot;, x0, y0, x1, y1, l0, l1, direction);<br>
<br>
                if ((l0 &lt; level &amp;&amp; l1 &lt; level) || (l0 &gt; level &amp;&amp; l1 &gt; level))<br>
                    throw new RuntimeException(&quot;implementation error&quot;);<br>
<br>
                if (l0 == level)<br>
                {<br>
                    x = data.lon + x0 * DEM.res;<br>
                    y = data.lat + y0 * DEM.res;<br>
                }<br>
                else<br>
                {<br>
                    double delta = (l0-level)/(l0-l1);<br>
                    x = data.lon + (x0 + delta * (x1-x0)) * DEM.res;<br>
                    y = data.lat + (y0 + delta * (y1-y0)) * DEM.res;<br>
                }<br>
                double dist = orthodromicDistance(x, y, lastX, lastY);<br>
                debug(&quot;levels: %d %d %f, %d %d %f: %f %f %f&quot;, x0, y0, l0, x1, y1, l1, x, y, dist);<br>
<br>
                if (dist &gt; 1)<br>
                {<br>
                    if (direction &gt; 0)<br>
                        points.add(0, new Coord(y, x));<br>
                    else<br>
                        points.add(points.size(), new Coord(y, x));<br>
                    lastX = x;<br>
                    lastY = y;<br>
                }<br>
            }<br>
        }<br>
<br>
<br>
        public Isolines(DEM data, int minX, int maxX, int minY, int maxY)<br>
        {<br>
            this.data = data;<br>
            this.minX = minX;<br>
            this.maxX = maxX;<br>
            this.minY = minY;<br>
            this.maxY = maxY;<br>
<br>
            init();<br>
        }<br>
<br>
        public Isolines(DEM data, double  minLat, double minLon, double maxLat, double maxLon)<br>
        {<br>
            System.out.printf(&quot;init: %f %f %f %f\n&quot;, minLat, minLon, maxLat, maxLon);<br>
<br>
            this.data = data;<br>
            this.minX = (int) ((minLon-data.lon)/data.res);<br>
            this.minY = (int) ((minLat-data.lat)/data.res);<br>
            this.maxX = (int) ((maxLon-data.lon)/data.res);<br>
            this.maxY = (int) ((maxLat-data.lat)/data.res);<br>
<br>
            init();<br>
        }<br>
<br>
        private void init()<br>
        {<br>
            System.out.printf(&quot;init: %d %d %d %d\n&quot;, minX, minY, maxX, maxY);<br>
            data.read(minX-2, minY-2, maxX+2, maxY+2);<br>
            // we need some overlap for bicubic interpolation<br>
            max = -1000;<br>
            min = 10000;<br>
            for (int i=minX; i&lt;maxX; i++)<br>
                for (int j=minY; j&lt;maxY; j++)<br>
                {<br>
                    if (data.elevation(i, j) &lt; min) min = data.elevation(i, j);<br>
                    if (data.elevation(i, j) &gt; max) max = data.elevation(i, j);<br>
                }<br>
<br>
            debug(&quot;min: %f, max: %f\n&quot;, min, max);<br>
        }<br>
<br>
        double getMinHeight()<br>
        {<br>
            return min;<br>
        }<br>
<br>
        double getMaxHeight()<br>
        {<br>
            return max;<br>
        }<br>
<br>
        public void addLevels(int start, int increment)<br>
        {<br>
            for (int level=start; level&lt;max; level+=increment)<br>
                addLevel(level);<br>
        }<br>
<br>
        private class Edge implements Brent.Function<br>
        {<br>
            double x0, y0, x1, y1, level;<br>
            Edge(double x0, double y0, double x1, double y1, double level)<br>
            {<br>
                this.x0 = x0;<br>
                this.y0 = y0;<br>
                this.x1 = x1;<br>
                this.y1 = y1;<br>
                this.level = level;<br>
            }<br>
<br>
            public double eval(double d)<br>
            {<br>
                double f = data.elevation(x0 + d * (x1-x0), y0 + d * (y1-y0)) - level;<br>
                //System.out.printf(&quot;evalEdge: %f %f\n&quot;, d, f);<br>
                return f;<br>
            }<br>
        }<br>
<br>
        private class Position<br>
        {<br>
            int ix, iy;<br>
            double x, y;<br>
            int edge;<br>
<br>
            Position(int ix, int iy, double x, double y, int edge)<br>
            {<br>
                this.ix = ix;<br>
                this.iy = iy;<br>
                this.x = x;<br>
                this.y = y;<br>
                this.edge = edge;<br>
            }<br>
<br>
            Position(Position p)<br>
            {<br>
                this.ix = p.ix;<br>
                this.iy = p.iy;<br>
                this.x = p.x;<br>
                this.y = p.y;<br>
                this.edge = p.edge;<br>
            }<br>
<br>
            void markEdge()<br>
            {<br>
                debug(&quot;marking edge: %d %d %d %d&quot;, ix, iy, edge, brd[edge]);<br>
                visited[iy*(N+1)+ix] |= brd[edge];<br>
            }<br>
<br>
            void moveCell()<br>
            {<br>
                markEdge();<br>
                ix += mov[edge][0];<br>
                iy += mov[edge][1];<br>
                edge = rev[edge];<br>
                markEdge();<br>
            }<br>
        }<br>
<br>
        byte visited[] = new byte[(N+1)*(N+1)];<br>
<br>
        public void addLevel(double level)<br>
        {<br>
            if (level &lt; min || level &gt; max)<br>
                return;<br>
<br>
            System.out.printf(&quot;addLevel: %f\n&quot;, level);<br>
            java.util.Arrays.fill(visited, (byte) 0);<br>
<br>
            for (int y=minY; y&lt;maxY; y++)<br>
            {<br>
                for (int x=minX; x&lt;maxX; x++)<br>
                {<br>
                    byte v = 0;<br>
                    // Mark the borders of the cell, represented by the four points (i, j), (i+1, j), (i, j+1), (i+1, j+1),<br>
                    // which are intersected by the contour. The values are:<br>
                    // 1: top<br>
                    // 2: left<br>
                    // 4: bottom<br>
                    // 8: right<br>
                    if (data.elevation(x, y) &gt;= level)<br>
                    {<br>
                        if (data.elevation(x+1, y) &lt; level) { v |= 1; }<br>
                        if (data.elevation(x, y+1) &lt; level) { v |= 2; }<br>
                    }<br>
                    else<br>
                    {<br>
                        if (data.elevation(x+1, y) &gt; level) { v |= 1; }<br>
                        if (data.elevation(x, y+1) &gt; level) { v |= 2; }<br>
                    }<br>
<br>
                    int k=-1;<br>
<br>
                    if ((v&amp;1) &gt; 0 &amp;&amp; (visited[y*(N+1)+x]&amp;1) == 0)<br>
                    {<br>
                        k=0;<br>
                    }<br>
                    else if ((v&amp;2) &gt; 0 &amp;&amp; (visited[y*(N+1)+x]&amp;2) == 0)<br>
                    {<br>
                        k=1;<br>
                    }<br>
<br>
                    if (k&gt;=0)<br>
                    {<br>
                        int x0 = x + off0[k][0];<br>
                        int y0 = y + off0[k][1];<br>
                        int x1 = x + off1[k][0];<br>
                        int y1 = y + off1[k][1];<br>
<br>
                        try {<br>
                            Brent.Function f = new Edge(data.lat + y0 * DEM.res, data.lon + x0 * DEM.res,<br>
                                                        data.lat + y1 * DEM.res, data.lon + x1 * DEM.res,<br>
                                                        level);<br>
                            double f0 = elevation(x0, y0) - level;<br>
                            double f1 = elevation(x1, y1) - level;<br>
                            double delta;<br>
                            if (Math.abs(f0) &lt; epsilon)<br>
                            {<br>
                                delta = 0;<br>
                            }<br>
                            else if (Math.abs(f1) &lt; epsilon)<br>
                                continue;<br>
                            else<br>
                                delta = Brent.zero(f, 0, 1-epsilon);<br>
<br>
                            Position p = new Position(x, y, data.lon + (x0+delta*(x1-x0))*DEM.res, data.lat + (y0+delta*(y1-y0))*DEM.res, k);<br>
                            p.markEdge();<br>
                            isolines.add(traceByStepping(level, p));<br>
                        }<br>
                        catch (RuntimeException ex)<br>
                        {<br>
                            debug(&quot;error: %s&quot;, ex.toString());<br>
                            ex.printStackTrace();<br>
                            return;<br>
                        }<br>
                    }<br>
                }<br>
            }<br>
        }<br>
<br>
        private Isoline traceByStepping(double level, Position p)<br>
        {<br>
            debug(&quot;traceByStepping: starting contour %f %d %d %f %f %d&quot;, level, p.ix, p.iy, p.x, p.y, p.edge);<br>
            int direction = 1;<br>
            int n = 0;<br>
            Position startP = new Position(p);<br>
            boolean foundEnd = false;<br>
<br>
            Isoline line = new Isoline(level);<br>
<br>
            while (true)<br>
            {<br>
                debug(&quot;traceByStepping: %f %d %d %f %f %d&quot;, level, p.ix, p.iy, p.x, p.y, p.edge);<br>
                visited[p.iy*(N+1)+p.ix] |= brd[p.edge];<br>
<br>
                if (n&gt;0 &amp;&amp; p.ix == startP.ix &amp;&amp; p.iy == startP.iy &amp;&amp; orthodromicDistance(p.x, p.y, startP.x, startP.y) &lt; 5)<br>
                {<br>
                    debug(&quot;closed curve!&quot;);<br>
                    line.close();<br>
                    break;<br>
                }<br>
                else if (p.ix &lt; minX || p.iy &lt; minY || p.ix &gt;= maxX || p.iy &gt;= maxY)<br>
                {<br>
                    if (foundEnd) // did we already reach one end?<br>
                    {<br>
                        debug(&quot;second border reached!&quot;);<br>
                        break;<br>
                    }<br>
                    else<br>
                    {<br>
                        debug(&quot;border reached!&quot;);<br>
                        foundEnd = true;<br>
                        n = 0;<br>
                        direction *= -1;<br>
                        p = new Position(startP);<br>
                        p.moveCell();<br>
                        continue;<br>
                    }<br>
                }<br>
                n++;<br>
                if (!line.addCell(p, direction) || line.points.size() &gt; maxPoints)<br>
                {<br>
                    debug(&quot;ending contour&quot;);<br>
                    isolines.add(line);<br>
                    return line;<br>
                }<br>
            }<br>
<br>
            return line;<br>
        }<br>
<br>
    }<br>
<br>
    /**<br>
     * Returns the orthodromic distance between two geographic coordinates in WGS84 datum.<br>
     * The orthodromic distance is the shortest distance between two points<br>
     * on a sphere&#39;s surface. The orthodromic path is always on a great circle.<br>
     *<br>
     * @param  x1 Longitude of first  point (in degrees).<br>
     * @param  y1 Latitude  of first  point (in degrees).<br>
     * @param  x2 Longitude of second point (in degrees).<br>
     * @param  y2 Latitude  of second point (in degrees).<br>
     * @return The orthodromic distance (in meters).<br>
     *<br>
     */<br>
    public static double orthodromicDistance(double x1, double y1, double x2, double y2)<br>
    {<br>
        x1 = Math.toRadians(x1);<br>
        y1 = Math.toRadians(y1);<br>
        x2 = Math.toRadians(x2);<br>
        y2 = Math.toRadians(y2);<br>
        final int MAX_ITERATIONS = 100;<br>
        final double EPS = 5.0E-14;<br>
        final double F = 1 / inverseFlattening;<br>
        final double R = 1 - F;<br>
        double tu1 = R * Math.sin(y1) / Math.cos(y1);<br>
        double tu2 = R * Math.sin(y2) / Math.cos(y2);<br>
        double cu1 = 1 / Math.sqrt(tu1 * tu1 + 1);<br>
        double cu2 = 1 / Math.sqrt(tu2 * tu2 + 1);<br>
        double su1 = cu1 * tu1;<br>
        double s = cu1 * cu2;<br>
        double baz = s * tu2;<br>
        double faz = baz * tu1;<br>
        double x = x2 - x1;<br>
        for (int i = 0; i &lt; MAX_ITERATIONS; i++)<br>
        {<br>
            final double sx = Math.sin(x);<br>
            final double cx = Math.cos(x);<br>
            tu1 = cu2 * sx;<br>
            tu2 = baz - su1 * cu2 * cx;<br>
            final double sy = Math.sqrt(tu1*tu1 + tu2*tu2);;<br>
            final double cy = s * cx + faz;<br>
            final double y = Math.atan2(sy, cy);<br>
            final double SA = s * sx / sy;<br>
            final double c2a = 1 - SA * SA;<br>
            double cz = faz + faz;<br>
            if (c2a &gt; 0) {<br>
                cz = -cz / c2a + cy;<br>
            }<br>
            double e = cz * cz * 2 - 1;<br>
            double c = ((-3 * c2a + 4) * F + 4) * c2a * F / 16;<br>
            double d = x;<br>
            x = ((e * cy * c + cz) * sy * c + y) * SA;<br>
            x = (1 - c) * x * F + x2 - x1;<br>
            if (Math.abs(d - x) &lt;= EPS) {<br>
                x = Math.sqrt((1 / (R * R) - 1) * c2a + 1) + 1;<br>
                x = (x - 2) / x;<br>
                c = 1 - x;<br>
                c = (x * x / 4 + 1) / c;<br>
                d = (0.375 * x * x - 1) * x;<br>
                x = e * cy;<br>
                s = 1 - 2 * e;<br>
                s = ((((sy * sy * 4 - 3) * s * cz * d / 6 - x) * d / 4 + cz) * sy * d + y) * c * R * semiMajorAxis;<br>
                return s;<br>
            }<br>
        }<br>
        final double LEPS = 1.0E-10;<br>
        if (Math.abs(x1 - x2) &lt;= LEPS &amp;&amp; Math.abs(y1 - y2) &lt;= LEPS) {<br>
            return 0;<br>
        }<br>
        if (Math.abs(y1) &lt;= LEPS &amp;&amp; Math.abs(y2) &lt;= LEPS) {<br>
            return Math.abs(x1 - x2) * semiMajorAxis;<br>
        }<br>
        throw new ArithmeticException(&quot;no convergence&quot;);<br>
    }<br>
<br>
}<br>/*<br>
 * Copyright (C) 2009 Christian Gawron<br>
 *<br>
 *  This program is free software; you can redistribute it and/or modify<br>
 *  it under the terms of the GNU General Public License version 2 as<br>
 *  published by the Free Software Foundation.<br>
 *<br>
 *  This program is distributed in the hope that it will be useful,<br>
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
 *  GNU General Public License for more details.<br>
 *<br>
 *<br>
 * Author: Christian Gawron<br>
 * Create date: 03-Jul-2009<br>
 */<br>
package uk.me.parabola.mkgmap.reader.dem;<br>
<br>
/**<br>
 * Find zero of a function using Brent&#39;s method.<br>
 */<br>
public class Brent<br>
{<br>
<br>
    public interface Function<br>
    {<br>
        public double eval(double x);<br>
    }<br>
<br>
<br>
    static double epsilon = 3.0e-10;<br>
<br>
    public static double zero(Function f, double x1, double x2)<br>
    {<br>
        return zero(f, x1, x2, 1e-8, 100);<br>
    }<br>
<br>
    public static double zero(Function f, double x1, double x2, double tol, int maxit)<br>
    {<br>
        double a=x1, b=x2, c=x2, d=0, e=0, min1, min2;<br>
        double fa=f.eval(a), fb=f.eval(b), fc;<br>
        double p, q, r, s, tolerance, xm;<br>
<br>
        if ((fa &gt; 0.0 &amp;&amp; fb &gt; 0.0) || (fa &lt; 0.0 &amp;&amp; fb &lt; 0.0))<br>
            throw new ArithmeticException(&quot;Root must be bracketed&quot;);<br>
<br>
        fc=fb;<br>
        for (int iterations=0; iterations &lt; maxit; iterations++)<br>
        {<br>
            if ((fb &gt; 0.0 &amp;&amp; fc &gt; 0.0) || (fb &lt; 0.0 &amp;&amp; fc &lt; 0.0)) {<br>
                c=a;<br>
                fc=fa;<br>
                e=d=b-a;<br>
            }<br>
            if (Math.abs(fc) &lt; Math.abs(fb)) {<br>
                a=b;<br>
                b=c;<br>
                c=a;<br>
                fa=fb;<br>
                fb=fc;<br>
                fc=fa;<br>
            }<br>
            tolerance=2.0*epsilon*Math.abs(b)+0.5*tol;<br>
            xm=0.5*(c-b);<br>
            if (Math.abs(xm) &lt;= tolerance || fb == 0.0) return b;<br>
            if (Math.abs(e) &gt;= tolerance &amp;&amp; Math.abs(fa) &gt; Math.abs(fb)) {<br>
                s=fb/fa;<br>
                if (a == c) {<br>
                    p=2.0*xm*s;<br>
                    q=1.0-s;<br>
                } else {<br>
                    q=fa/fc;<br>
                    r=fb/fc;<br>
                    p=s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));<br>
                    q=(q-1.0)*(r-1.0)*(s-1.0);<br>
                }<br>
                if (p &gt; 0.0) q = -q;<br>
                p=Math.abs(p);<br>
                min1=3.0*xm*q-Math.abs(tolerance*q);<br>
                min2=Math.abs(e*q);<br>
                if (2.0*p &lt; (min1 &lt; min2 ? min1 : min2)) {<br>
                    e=d;<br>
                    d=p/q;<br>
                } else {<br>
                    d=xm;<br>
                    e=d;<br>
                }<br>
            } else {<br>
                d=xm;<br>
                e=d;<br>
            }<br>
            a=b;<br>
            fa=fb;<br>
            if (Math.abs(d) &gt; tolerance)<br>
                b += d;<br>
            else<br>
                b += xm &gt;= 0 ? tolerance : -tolerance;<br>
            fb=f.eval(b);<br>
        }<br>
        throw new ArithmeticException(&quot;Maximum number of iterations exceeded&quot;);<br>
    }<br>
}<br>_______________________________________________<br>
mkgmap-dev mailing list<br>
<a href="mailto:mkgmap-dev@lists.mkgmap.org.uk">mkgmap-dev@lists.mkgmap.org.uk</a><br>
<a href="http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev" target="_blank">http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev</a><br></blockquote></div><br>