[Git][debian-gis-team/mkgmap-splitter][master] 12 commits: Update branch in gbp.conf & Vcs-Git URL.
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Sun Aug 15 11:12:32 BST 2021
Bas Couwenberg pushed to branch master at Debian GIS Project / mkgmap-splitter
Commits:
f768548f by Bas Couwenberg at 2021-06-01T06:00:56+02:00
Update branch in gbp.conf & Vcs-Git URL.
- - - - -
1f4db4eb by Bas Couwenberg at 2021-06-01T06:01:49+02:00
New upstream version 0.0.0+svn602
- - - - -
29e50db9 by Bas Couwenberg at 2021-06-01T06:01:51+02:00
Update upstream source from tag 'upstream/0.0.0+svn602'
Update to upstream version '0.0.0+svn602'
with Debian dir c0eed8747816400d525a0a8488588d2137fce192
- - - - -
2ae22ba0 by Bas Couwenberg at 2021-06-01T06:09:54+02:00
New upstream SVN snapshot.
- - - - -
c2d6f64c by Bas Couwenberg at 2021-06-01T06:12:24+02:00
Set distribution to experimental.
- - - - -
fbfad6b2 by Bas Couwenberg at 2021-07-01T07:20:26+02:00
New upstream version 0.0.0+svn615
- - - - -
0b721c33 by Bas Couwenberg at 2021-07-01T07:20:28+02:00
Update upstream source from tag 'upstream/0.0.0+svn615'
Update to upstream version '0.0.0+svn615'
with Debian dir 1ea580756f0d7f82c7e4ef7176aaef2340c3eef1
- - - - -
6639fe5b by Bas Couwenberg at 2021-07-01T07:20:47+02:00
New upstream SVN snapshot.
- - - - -
2ab41c7c by Bas Couwenberg at 2021-07-01T07:22:08+02:00
Set distribution to experimental.
- - - - -
d0787af3 by Bas Couwenberg at 2021-08-15T11:53:59+02:00
Revert "Update branch in gbp.conf & Vcs-Git URL."
This reverts commit f768548f69798443ee225922e13c5de413621cc5.
- - - - -
b076ecc8 by Bas Couwenberg at 2021-08-15T11:54:09+02:00
Move from experimental to unstable.
- - - - -
404b6870 by Bas Couwenberg at 2021-08-15T12:05:59+02:00
No-change rebuild for source-only upload.
- - - - -
15 changed files:
- build.xml
- debian/changelog
- ivysettings.xml
- resources/splitter-version.properties
- src/uk/me/parabola/splitter/OSMFileHandler.java
- src/uk/me/parabola/splitter/parser/BinaryMapParser.java
- src/uk/me/parabola/splitter/parser/O5mMapParser.java
- src/uk/me/parabola/splitter/solver/AreasCalculator.java
- src/uk/me/parabola/splitter/solver/DensityMap.java
- src/uk/me/parabola/splitter/solver/SplittableDensityArea.java
- src/uk/me/parabola/splitter/writer/AbstractOSMWriter.java
- src/uk/me/parabola/splitter/writer/BinaryMapWriter.java
- src/uk/me/parabola/splitter/writer/O5mMapWriter.java
- src/uk/me/parabola/splitter/writer/OSMXMLWriter.java
- test/uk/me/parabola/splitter/tools/SparseBitSetTest.java
Changes:
=====================================
build.xml
=====================================
@@ -341,10 +341,10 @@
<fileset dir="${test.input.cache}" includes="osm/*.osm.pbf" />
</copy>
<mkdir dir="test/resources/in/osm"/>
- <get src="http://www.mkgmap.org.uk/testinput/osm/alaska-2016-12-27.osm.pbf"
+ <get src="https://www.mkgmap.org.uk/testinput/osm/alaska-2016-12-27.osm.pbf"
dest="test/resources/in/osm/alaska-2016-12-27.osm.pbf" usetimestamp="true"
ignoreerrors="true"/>
- <get src="http://www.mkgmap.org.uk/testinput/osm/hamburg-2016-12-26.osm.pbf"
+ <get src="https://www.mkgmap.org.uk/testinput/osm/hamburg-2016-12-26.osm.pbf"
dest="test/resources/in/osm/hamburg-2016-12-26.osm.pbf" usetimestamp="true"
ignoreerrors="true"/>
</target>
=====================================
debian/changelog
=====================================
@@ -1,3 +1,27 @@
+mkgmap-splitter (0.0.0+svn615-2) unstable; urgency=medium
+
+ * No-change rebuild for source-only upload.
+
+ -- Bas Couwenberg <sebastic at debian.org> Sun, 15 Aug 2021 12:05:51 +0200
+
+mkgmap-splitter (0.0.0+svn615-1) unstable; urgency=medium
+
+ * Move from experimental to unstable.
+
+ -- Bas Couwenberg <sebastic at debian.org> Sun, 15 Aug 2021 11:54:01 +0200
+
+mkgmap-splitter (0.0.0+svn615-1~exp1) experimental; urgency=medium
+
+ * New upstream SVN snapshot.
+
+ -- Bas Couwenberg <sebastic at debian.org> Thu, 01 Jul 2021 07:21:55 +0200
+
+mkgmap-splitter (0.0.0+svn602-1~exp1) experimental; urgency=medium
+
+ * New upstream SVN snapshot.
+
+ -- Bas Couwenberg <sebastic at debian.org> Tue, 01 Jun 2021 06:12:12 +0200
+
mkgmap-splitter (0.0.0+svn598-1) unstable; urgency=medium
* New upstream SVN snapshot.
=====================================
ivysettings.xml
=====================================
@@ -1,5 +1,5 @@
<ivysettings>
- <property name="mkgmap.ivy.repo" value="http://ivy.mkgmap.org.uk/repo" />
+ <property name="mkgmap.ivy.repo" value="https://ivy.mkgmap.org.uk/repo" />
<settings defaultResolver="custom" />
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
=====================================
resources/splitter-version.properties
=====================================
@@ -1,2 +1,2 @@
-svn.version: 598
-build.timestamp: 2021-02-02T15:27:27+0000
+svn.version: 615
+build.timestamp: 2021-06-30T10:03:46+0100
=====================================
src/uk/me/parabola/splitter/OSMFileHandler.java
=====================================
@@ -81,7 +81,7 @@ public class OSMFileHandler {
O5mMapParser o5mParser = new O5mMapParser(processor, fileChannel, skipArray);
o5mParser.parse();
if (skipArray == null) {
- skipArray = o5mParser.getSkipArray();
+ skipArray = o5mParser.getNextSkipArray();
skipArrayMap.put(filename, skipArray);
}
}
=====================================
src/uk/me/parabola/splitter/parser/BinaryMapParser.java
=====================================
@@ -57,13 +57,13 @@ public class BinaryMapParser extends BinaryParser {
this.skipRels = processor.skipRels();
this.msgLevel = msgLevel;
- if (skipNodes == false) {
+ if (!skipNodes) {
wantedTypeMask |= TYPE_DENSE;
wantedTypeMask |= TYPE_NODES;
}
- if (skipWays == false)
+ if (!skipWays)
wantedTypeMask |= TYPE_WAYS;
- if (skipRels == false)
+ if (!skipRels)
wantedTypeMask |= TYPE_RELS;
}
@@ -81,11 +81,9 @@ public class BinaryMapParser extends BinaryParser {
if (blockType != 0 && (blockType & wantedTypeMask) == 0)
return true;
} else if (blockType != -1) {
- // System.out.println("previous block contained " + blockType );
blockTypes.add(blockType);
}
blockType = 0;
- // System.out.println("Seeing block of type: "+block.getType());
if (block.getType().equals("OSMData"))
return false;
if (block.getType().equals("OSMHeader"))
@@ -101,40 +99,36 @@ public class BinaryMapParser extends BinaryParser {
// So do nothing else.
}
- // Per-block state for parsing, set when processing the header of a block;
@Override
protected void parseDense(Osmformat.DenseNodes nodes) {
blockType |= TYPE_DENSE;
if (skipNodes)
return;
- long last_id = 0, last_lat = 0, last_lon = 0;
+ long lastId = 0, lastLat = 0, lastLon = 0;
int j = 0;
int maxi = nodes.getIdCount();
- Node tmp = new Node();
for (int i = 0; i < maxi; i++) {
- long lat = nodes.getLat(i) + last_lat;
- last_lat = lat;
- long lon = nodes.getLon(i) + last_lon;
- last_lon = lon;
- long id = nodes.getId(i) + last_id;
- last_id = id;
+ long lat = nodes.getLat(i) + lastLat;
+ lastLat = lat;
+ long lon = nodes.getLon(i) + lastLon;
+ lastLon = lon;
+ long id = nodes.getId(i) + lastId;
+ lastId = id;
double latf = parseLat(lat), lonf = parseLon(lon);
- tmp = new Node();
+ Node tmp = new Node();
tmp.set(id, latf, lonf);
if (nodes.hasDenseinfo())
tmp.setVersion(nodes.getDenseinfo().getVersion(i));
- if (!skipTags) {
- if (nodes.getKeysValsCount() > 0) {
- while (nodes.getKeysVals(j) != 0) {
- int keyid = nodes.getKeysVals(j++);
- int valid = nodes.getKeysVals(j++);
- tmp.addTag(getStringById(keyid), getStringById(valid));
- }
- j++; // Skip over the '0' delimiter.
-
+ if (!skipTags && nodes.getKeysValsCount() > 0) {
+ while (nodes.getKeysVals(j) != 0) {
+ int keyid = nodes.getKeysVals(j++);
+ int valid = nodes.getKeysVals(j++);
+ tmp.addTag(getStringById(keyid), getStringById(valid));
}
+ j++; // Skip over the '0' delimiter.
+
}
processor.processNode(tmp);
elemCounter.countNode(tmp.getId());
@@ -143,7 +137,7 @@ public class BinaryMapParser extends BinaryParser {
@Override
protected void parseNodes(List<Osmformat.Node> nodes) {
- if (nodes.size() == 0)
+ if (nodes.isEmpty())
return;
blockType |= TYPE_NODES;
if (skipNodes)
@@ -174,14 +168,14 @@ public class BinaryMapParser extends BinaryParser {
return;
for (Osmformat.Way i : ways) {
Way tmp = new Way();
- if (skipTags == false) {
+ if (!skipTags) {
for (int j = 0; j < i.getKeysCount(); j++)
tmp.addTag(getStringById(i.getKeys(j)), getStringById(i.getVals(j)));
}
- long last_id = 0;
+ long lastId = 0;
for (long j : i.getRefsList()) {
- tmp.addRef(j + last_id);
- last_id = j + last_id;
+ tmp.addRef(j + lastId);
+ lastId = j + lastId;
}
long id = i.getId();
@@ -196,14 +190,14 @@ public class BinaryMapParser extends BinaryParser {
@Override
protected void parseRelations(List<Osmformat.Relation> rels) {
- if (rels.size() == 0)
+ if (rels.isEmpty())
return;
blockType |= TYPE_RELS;
if (skipRels)
return;
for (Osmformat.Relation i : rels) {
Relation tmp = new Relation();
- if (skipTags == false) {
+ if (!skipTags) {
for (int j = 0; j < i.getKeysCount(); j++)
tmp.addTag(getStringById(i.getKeys(j)), getStringById(i.getVals(j)));
}
@@ -211,10 +205,10 @@ public class BinaryMapParser extends BinaryParser {
tmp.setId(id);
tmp.setVersion(i.getInfo().getVersion());
- long last_mid = 0;
+ long lastMemId = 0;
for (int j = 0; j < i.getMemidsCount(); j++) {
- long mid = last_mid + i.getMemids(j);
- last_mid = mid;
+ long mid = lastMemId + i.getMemids(j);
+ lastMemId = mid;
String role = getStringById(i.getRolesSid(j));
String etype = null;
=====================================
src/uk/me/parabola/splitter/parser/O5mMapParser.java
=====================================
@@ -16,6 +16,7 @@ package uk.me.parabola.splitter.parser;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import uk.me.parabola.splitter.Area;
@@ -93,7 +94,8 @@ public class O5mMapParser {
private long[] lastRef;
private long lastTs;
private long lastChangeSet;
- private int lastLon, lastLat;
+ private int lastLon;
+ private int lastLat;
/**
* A parser for the o5m format.
@@ -101,9 +103,8 @@ public class O5mMapParser {
* @param fc the file channel for the input file
* @param skipArray An Array of longs that is used to hold information of file position of the first occurrence of
* each known 05m data type (esp. nodes, ways, and relations).
- * @throws IOException
*/
- public O5mMapParser(MapProcessor processor, FileChannel fc, long[] skipArray) throws IOException{
+ public O5mMapParser(MapProcessor processor, FileChannel fc, long[] skipArray) {
this.fileChannel = fc;
this.processor = processor;
this.skipArray = skipArray;
@@ -130,13 +131,11 @@ public class O5mMapParser {
int start = get() & 0xff;
if (start != RESET_FLAG)
throw new IOException("wrong header byte " + start);
- if (skipArray != null) {
- if (skipNodes) {
- if (skipWays)
- filePos = skipArray[REL_DATASET]; // jump to first relation
- else
- filePos = skipArray[WAY_DATASET]; // jump to first way
- }
+ if (skipArray != null && skipNodes) {
+ if (skipWays)
+ filePos = skipArray[REL_DATASET]; // jump to first relation
+ else
+ filePos = skipArray[WAY_DATASET]; // jump to first way
}
if (filePos >= 0)
readFile();
@@ -152,20 +151,17 @@ public class O5mMapParser {
long size = 0;
int fileType = get() & 0xff;
if (fileType >= 0 && fileType < 0xf0) {
- if (skipArray == null) {
+ if (skipArray == null && firstPosInFile[fileType] == -1) {
// save first occurrence of a data set type
- if (firstPosInFile[fileType] == -1) {
- firstPosInFile[fileType] = Math.max(0, filePos- 1);
- }
+ firstPosInFile[fileType] = Math.max(0, filePos- 1);
}
size = readUnsignedNum64();
nextFilePos = filePos + size;
- boolean doSkip = false;
- if (fileType == NODE_DATASET && skipNodes) doSkip = true;
- else if (fileType == WAY_DATASET && skipWays) doSkip = true;
- else if (fileType == REL_DATASET && skipRels) doSkip = true;
+ boolean doSkip = ((fileType == NODE_DATASET && skipNodes)
+ || (fileType == WAY_DATASET && skipWays)
+ || (fileType == REL_DATASET && skipRels));
switch(fileType) {
case NODE_DATASET:
case WAY_DATASET:
@@ -456,7 +452,7 @@ public class O5mMapParser {
while (true) {
final int b = get();
if (b == 0)
- return new String(cnvBuffer, 0, length, "UTF-8");
+ return new String(cnvBuffer, 0, length, StandardCharsets.UTF_8);
cnvBuffer[length++] = (byte) b;
}
@@ -590,8 +586,8 @@ public class O5mMapParser {
return result;
}
- public long[] getSkipArray() {
- return firstPosInFile;
+ public long[] getNextSkipArray() {
+ return firstPosInFile;
}
/**
=====================================
src/uk/me/parabola/splitter/solver/AreasCalculator.java
=====================================
@@ -169,8 +169,8 @@ public class AreasCalculator {
if (exactArea != null)
System.out.println("Exact map coverage after applying bounding box of polygon-file is " + exactArea);
else {
- System.out.println("Exact map coverage after applying bounding box of polygon-file is an empty area");
- return;
+ throw new SplitFailedException(
+ "Exact map coverage after applying bounding box of polygon-file is an empty area");
}
}
=====================================
src/uk/me/parabola/splitter/solver/DensityMap.java
=====================================
@@ -13,6 +13,7 @@
package uk.me.parabola.splitter.solver;
+import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileInputStream;
@@ -21,6 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
+import java.util.List;
import java.util.regex.Pattern;
import uk.me.parabola.splitter.Area;
@@ -63,17 +65,17 @@ public class DensityMap {
* @param polygonArea the polygon area
* @return an area with rectilinear shape that approximates the polygon area
*/
- public java.awt.geom.Area rasterPolygon(java.awt.geom.Area polygonArea){
+ public java.awt.geom.Area rasterPolygon(java.awt.geom.Area polygonArea) {
if (polygonArea == null)
return null;
java.awt.geom.Area simpleArea = new java.awt.geom.Area();
if (polygonArea.intersects(Utils.area2Rectangle(bounds, 0)) == false)
return simpleArea;
int gridElemWidth = bounds.getWidth() / width;
- int gridElemHeight= bounds.getHeight()/ height;
+ int gridElemHeight = bounds.getHeight() / height;
Rectangle polygonBbox = polygonArea.getBounds();
- int minLat = Math.max((int)polygonBbox.getMinY(), bounds.getMinLat());
- int maxLat = Math.min((int)polygonBbox.getMaxY(), bounds.getMaxLat());
+ int minLat = Math.max((int) polygonBbox.getMinY(), bounds.getMinLat());
+ int maxLat = Math.min((int) polygonBbox.getMaxY(), bounds.getMaxLat());
int minY = latToY(minLat);
int maxY = latToY(maxLat);
assert minY >= 0 && minY <= height;
@@ -82,7 +84,7 @@ public class DensityMap {
int lon = xToLon(x);
if (lon + gridElemWidth < polygonBbox.getMinX()
|| lon > polygonBbox.getMaxX()
- || polygonArea.intersects(lon, polygonBbox.getMinY(), gridElemWidth, polygonBbox.getHeight()) == false){
+ || !polygonArea.intersects(lon, polygonBbox.getMinY(), gridElemWidth, polygonBbox.getHeight())) {
continue;
}
int firstY = -1;
@@ -103,6 +105,14 @@ public class DensityMap {
simpleArea.add(new java.awt.geom.Area(new Rectangle(x,firstY,1,height-firstY)));
}
}
+ if (!simpleArea.isSingular()) {
+ List<List<Point>> shapes = Utils.areaToShapes(simpleArea);
+ if (shapes.removeIf(s -> !Utils.clockwise(s))) {
+ System.out.println("Warning: Rastered polaygon area contains holes, polygon is probably concave, trying to fix this");
+ simpleArea.reset();
+ shapes.forEach(s -> simpleArea.add(Utils.shapeToArea(s)));
+ }
+ }
return simpleArea;
}
@@ -147,11 +157,11 @@ public class DensityMap {
return nodeMap[x] != null ? nodeMap[x][y] : 0;
}
- public int[][] getyxMap(){
+ public int[][] getyxMap() {
int[][] yxMap = new int[height][];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
- int count = (nodeMap[x] == null) ? 0:nodeMap[x][y];
+ int count = (nodeMap[x] == null) ? 0 : nodeMap[x][y];
if (count > 0) {
if (yxMap[y] == null)
yxMap[y] = new int[width];
=====================================
src/uk/me/parabola/splitter/solver/SplittableDensityArea.java
=====================================
@@ -13,12 +13,6 @@
package uk.me.parabola.splitter.solver;
-import it.unimi.dsi.fastutil.ints.IntArrayList;
-import uk.me.parabola.splitter.Area;
-import uk.me.parabola.splitter.RoundingUtils;
-import uk.me.parabola.splitter.SplitFailedException;
-import uk.me.parabola.splitter.Utils;
-
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
@@ -28,24 +22,30 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import uk.me.parabola.splitter.Area;
+import uk.me.parabola.splitter.RoundingUtils;
+import uk.me.parabola.splitter.SplitFailedException;
+import uk.me.parabola.splitter.Utils;
+
/**
- * Splits a density map into multiple areas, none of which
- * exceed the desired threshold.
+ * Splits a density map into multiple areas, none of which exceed the desired
+ * threshold.
*
-* @author Chris Miller, Gerd Petermann
+ * @author Chris Miller, Gerd Petermann
*/
public class SplittableDensityArea {
private static final int MAX_LAT_DEGREES = 85;
private static final int MAX_LON_DEGREES = 90;
public static final int MAX_SINGLE_POLYGON_VERTICES = 40;
- private static final int MAX_LOOPS = 100; // number of loops to find better solution for one rectangular area
- private static final int AXIS_HOR = 0;
- private static final int AXIS_VERT = 1;
+ private static final int MAX_LOOPS = 100; // number of loops to find better solution for one rectangular area
+ private static final int AXIS_HOR = 0;
+ private static final int AXIS_VERT = 1;
public static final double NICE_MAX_ASPECT_RATIO = 4;
private static final double VERY_NICE_FILL_RATIO = 0.93;
private static final long LARGE_MAX_NODES = 10_000_000;
private static final int GOOD_SOL_INIT_SIZE = 1_000_000;
-
+
private double maxAspectRatio;
private long minNodes;
private final int startSearchLimit;
@@ -54,30 +54,30 @@ public class SplittableDensityArea {
private final DensityMap allDensities;
private EnhancedDensityMap extraDensityInfo;
-
+
private boolean beQuiet = false;
private long maxNodes;
private final int shift;
-
+
private HashSet<Tile> knownBad;
private LinkedHashMap<Tile, Integer> incomplete;
private long countBad;
-
+
/** if true enables an alternative algorithm */
private boolean searchAll = false;
-
+
final int maxTileHeight;
final int maxTileWidth;
-
- private HashMap<Tile,Solution> goodSolutions;
- private double goodRatio;
+
+ private HashMap<Tile, Solution> goodSolutions;
+ private double goodRatio;
private boolean trimShape;
private boolean trimTiles;
private boolean allowEmptyPart = false;
private int currMapId;
private boolean hasEmptyPart;
private boolean ignoreSize;
-
+
public SplittableDensityArea(DensityMap densities, int startSearchLimit) {
this.shift = densities.getShift();
this.searchLimit = this.startSearchLimit = startSearchLimit;
@@ -85,9 +85,11 @@ public class SplittableDensityArea {
maxTileWidth = Utils.toMapUnit(MAX_LON_DEGREES) / (1 << shift);
allDensities = densities;
}
+
public DensityMap getAllDensities() {
return allDensities;
}
+
public void setMapId(int mapId) {
currMapId = mapId;
}
@@ -96,51 +98,52 @@ public class SplittableDensityArea {
this.maxNodes = maxNodes;
}
-
public void setTrim(boolean trim) {
this.trimShape = trim;
}
- public boolean hasData(){
+ public boolean hasData() {
return allDensities != null && allDensities.getNodeCount() > 0;
}
/**
* @return the area that this splittable area represents
- */
+ */
public Area getBounds() {
return allDensities.getBounds();
}
/**
- * @param maxNodes the maximum number of nodes per area
+ * Create the list of areas.
+ *
* @return a list of areas, each containing no more than {@code maxNodes} nodes.
- * Each area returned must be aligned to the appropriate overview map resolution.
- */
+ * Each area returned must be aligned to the appropriate overview map
+ * resolution.
+ */
private List<Area> split() {
if (allDensities == null || allDensities.getNodeCount() == 0)
return Collections.emptyList();
prepare(null);
Tile startTile = new Tile(extraDensityInfo);
List<Tile> startTiles = new ArrayList<>();
- if (trimShape || allDensities.getBounds().getWidth() >= 0x1000000){
- // if trim is wanted or tile spans over planet
- // we try first to find large empty areas (sea)
+ if (trimShape || allDensities.getBounds().getWidth() >= 0x1000000) {
+ // if trim is wanted or tile spans over planet
+ // we try first to find large empty areas (sea)
startTiles.addAll(checkForEmptyClusters(0, startTile, true));
- }
- else
+ } else {
startTiles.add(startTile);
-
+ }
+
Solution fullSolution = new Solution(maxNodes);
int countNoSol;
- while (true){
+ while (true) {
countNoSol = 0;
- for (Tile tile: startTiles){
+ for (Tile tile : startTiles) {
hasEmptyPart = false;
if (!beQuiet)
System.out.println("Solving partition " + tile.toString());
Solution solution = solveRectangularArea(tile);
- if (solution != null && solution.isEmpty() == false)
+ if (solution != null && !solution.isEmpty())
fullSolution.merge(solution);
else {
countNoSol++;
@@ -150,42 +153,47 @@ public class SplittableDensityArea {
}
if (countNoSol == 0)
break;
- if (allowEmptyPart || hasEmptyPart == false)
+ if (allowEmptyPart || !hasEmptyPart)
break;
allowEmptyPart = true;
fullSolution = new Solution(maxNodes);
}
- if(countNoSol > 0)
+ if (countNoSol > 0)
throw new SplitFailedException("Failed to find a correct split");
- System.out.println("Final solution: " + fullSolution.toString());
+ System.out.println("Final solution: " + fullSolution.toString());
if (fullSolution.isNice())
System.out.println("This seems to be nice.");
return getAreas(fullSolution, null);
}
/**
- * Split with a given polygon and max nodes threshold. If the polygon
- * is not singular, it is divided into singular areas.
- * @param maxNodes
+ * Split with a given polygon and max nodes threshold. If the polygon is not
+ * singular, it is divided into singular areas.
+ *
* @param polygonArea
- * @return
+ * @return list of areas
*/
private List<Area> split(java.awt.geom.Area polygonArea) {
if (polygonArea == null)
return split();
- if (polygonArea.isSingular()){
+ if (polygonArea.isSingular()) {
java.awt.geom.Area rasteredArea = allDensities.rasterPolygon(polygonArea);
- if (rasteredArea.isEmpty()){
+ if (rasteredArea.isEmpty()) {
System.err.println("Bounding polygon doesn't intersect with the bounding box of the input file(s)");
return Collections.emptyList();
}
-
- prepare(polygonArea);
- Tile tile = new Tile(extraDensityInfo, rasteredArea.getBounds());
- Solution solution = findSolutionWithSinglePolygon(0, tile, rasteredArea);
- return getAreas(solution, polygonArea);
+ if (rasteredArea.isSingular()) {
+ prepare(polygonArea);
+ Tile tile = new Tile(extraDensityInfo, rasteredArea.getBounds());
+ Solution solution = findSolutionWithSinglePolygon(0, tile, rasteredArea);
+ if (solution == null && rasteredArea.isRectangular())
+ return split();
+ if (solution != null) {
+ return getAreas(solution, polygonArea);
+ }
+ }
}
- if (polygonArea.intersects(Utils.area2Rectangle(allDensities.getBounds(),0)))
+ if (polygonArea.intersects(Utils.area2Rectangle(allDensities.getBounds(), 0)))
return splitPolygon(polygonArea);
System.err.println("Bounding polygon doesn't intersect with the bounding box of the input file(s)");
return Collections.emptyList();
@@ -193,10 +201,10 @@ public class SplittableDensityArea {
/**
* Split a list of named polygons. Overlapping areas of the polygons are
- * extracted and each one is split for itself. A polygon may not be singular.
- * @param maxNodes
+ * extracted and each one is split for itself. A polygon may not be singular.
+ *
* @param namedPolygons
- * @return
+ * @return list of areas
*/
public List<Area> split(List<PolygonDesc> namedPolygons) {
if (namedPolygons.isEmpty())
@@ -207,19 +215,19 @@ public class SplittableDensityArea {
final IntArrayList sharedBy = new IntArrayList();
}
List<ShareInfo> sharedParts = new ArrayList<>();
- for (int i = 0; i < namedPolygons.size(); i++){
+ for (int i = 0; i < namedPolygons.size(); i++) {
boolean wasDistinct = true;
PolygonDesc namedPart = namedPolygons.get(i);
- java.awt.geom.Area distinctPart = new java.awt.geom.Area (namedPart.getArea());
- for(int j = 0; j < namedPolygons.size(); j++){
+ java.awt.geom.Area distinctPart = new java.awt.geom.Area(namedPart.getArea());
+ for (int j = 0; j < namedPolygons.size(); j++) {
if (j == i)
continue;
java.awt.geom.Area test = new java.awt.geom.Area(namedPart.getArea());
test.intersect(namedPolygons.get(j).getArea());
- if (test.isEmpty() == false){
+ if (!test.isEmpty()) {
wasDistinct = false;
distinctPart.subtract(namedPolygons.get(j).getArea());
- if (j > i){
+ if (j > i) {
ShareInfo si = new ShareInfo();
si.area = test;
si.sharedBy.add(i);
@@ -228,27 +236,27 @@ public class SplittableDensityArea {
}
}
}
- if (distinctPart.isEmpty() == false && distinctPart.intersects(Utils.area2Rectangle(allDensities.getBounds(),0))){
-// KmlWriter.writeKml("e:/ld_sp/distinct_"+namedPart.name, "distinct", distinctPart);
- if (wasDistinct == false)
+ if (!distinctPart.isEmpty() && distinctPart.intersects(Utils.area2Rectangle(allDensities.getBounds(), 0))) {
+// KmlWriter.writeKml("e:/ld_sp/distinct_"+namedPart.getName(), "distinct", distinctPart);
+ if (!wasDistinct)
System.out.println("splitting distinct part of " + namedPart.getName());
- else
+ else
System.out.println("splitting " + namedPart.getName());
result.addAll(split(distinctPart));
}
}
-
- for (int i = 0; i < sharedParts.size(); i++){
+
+ for (int i = 0; i < sharedParts.size(); i++) {
ShareInfo si = sharedParts.get(i);
int last = namedPolygons.size(); // list is extended in the loop
- for (int j = 0; j < last; j++){
+ for (int j = 0; j < last; j++) {
if (si.sharedBy.contains(j))
continue;
java.awt.geom.Area test = new java.awt.geom.Area(si.area);
test.intersect(namedPolygons.get(j).getArea());
- if (test.isEmpty() == false){
+ if (!test.isEmpty()) {
si.area.subtract(test);
- if (j > si.sharedBy.getInt(si.sharedBy.size()-1)){
+ if (j > si.sharedBy.getInt(si.sharedBy.size() - 1)) {
ShareInfo si2 = new ShareInfo();
si2.area = test;
si2.sharedBy.addAll(si.sharedBy);
@@ -259,11 +267,11 @@ public class SplittableDensityArea {
if (si.area.isEmpty())
break;
}
- if (si.area.isEmpty() == false && si.area.intersects(Utils.area2Rectangle(allDensities.getBounds(),0))){
+ if (!si.area.isEmpty() && si.area.intersects(Utils.area2Rectangle(allDensities.getBounds(), 0))) {
String desc = "";
for (int pos : si.sharedBy)
desc += namedPolygons.get(pos).getName() + " and ";
- desc = desc.substring(0,desc.lastIndexOf(" and"));
+ desc = desc.substring(0, desc.lastIndexOf(" and"));
System.out.println("splitting area shared by exactly " + si.sharedBy.size() + " polygons: " + desc);
// KmlWriter.writeKml("e:/ld_sp/shared_"+desc.replace(" " , "_"), desc, si.area);
result.addAll(split(si.area));
@@ -273,21 +281,21 @@ public class SplittableDensityArea {
}
/**
- * Split a list of named polygons into a given number of tiles.
- * This is probably only useful with an empty list of polygons
- * or a list containing one polygon.
+ * Split a list of named polygons into a given number of tiles. This is probably
+ * only useful with an empty list of polygons or a list containing one polygon.
+ *
* @param wantedTiles
- * @return
+ * @return list of areas
*/
public List<Area> split(int wantedTiles) {
long currMaxNodes = this.allDensities.getNodeCount() / wantedTiles;
class Pair {
long maxNodes;
int numTiles;
-
- Pair(long maxNodes, int numTiles){
+
+ Pair(long maxNodes, int numTiles) {
this.maxNodes = maxNodes;
- this.numTiles = numTiles;
+ this.numTiles = numTiles;
}
}
Pair bestBelow = null;
@@ -296,55 +304,53 @@ public class SplittableDensityArea {
ignoreSize = true;
while (true) {
setMaxNodes(currMaxNodes);
- System.out.println("Trying a max-nodes value of " + currMaxNodes + " to split " + allDensities.getNodeCount() + " nodes into " + wantedTiles + " areas");
+ System.out.println("Trying a max-nodes value of " + currMaxNodes + " to split "
+ + allDensities.getNodeCount() + " nodes into " + wantedTiles + " areas");
List<Area> res = split();
- if (res.isEmpty() || res.size() == wantedTiles){
+ if (res.isEmpty() || res.size() == wantedTiles) {
beQuiet = false;
res = split();
return res;
}
goodSolutions = new HashMap<>(GOOD_SOL_INIT_SIZE);
Pair pair = new Pair(currMaxNodes, res.size());
- if (res.size() > wantedTiles){
- if (bestAbove == null)
- bestAbove = pair;
- else if (bestAbove.numTiles > pair.numTiles)
- bestAbove = pair;
- else if (bestAbove.numTiles == pair.numTiles && pair.maxNodes < bestAbove.maxNodes)
+ if (res.size() > wantedTiles) {
+ if (bestAbove == null || bestAbove.numTiles > pair.numTiles
+ || (bestAbove.numTiles == pair.numTiles && pair.maxNodes < bestAbove.maxNodes))
bestAbove = pair;
} else {
- if (bestBelow == null)
- bestBelow = pair;
- else if (bestBelow.numTiles < pair.numTiles)
- bestBelow = pair;
- else if (bestBelow.numTiles == pair.numTiles && pair.maxNodes > bestBelow.maxNodes)
+ if (bestBelow == null || bestBelow.numTiles < pair.numTiles
+ || (bestBelow.numTiles == pair.numTiles && pair.maxNodes > bestBelow.maxNodes))
bestBelow = pair;
}
long testMaxNodes;
if (bestBelow == null || bestAbove == null)
- testMaxNodes = Math.min(Math.round((double) currMaxNodes * res.size() / wantedTiles), this.allDensities.getNodeCount()-1);
- else
+ testMaxNodes = Math.min(Math.round((double) currMaxNodes * res.size() / wantedTiles),
+ this.allDensities.getNodeCount() - 1);
+ else
testMaxNodes = (bestBelow.maxNodes + bestAbove.maxNodes) / 2;
- if (testMaxNodes == currMaxNodes){
+ if (testMaxNodes == currMaxNodes) {
System.err.println("Cannot find a good split with exactly " + wantedTiles + " areas");
return res;
}
currMaxNodes = testMaxNodes;
- }
+ }
}
- /**
- * Filter the density data, calculate once complex trigonometric results
+ /**
+ * Filter the density data, calculate once complex trigonometric results
+ *
* @param polygonArea
*/
- private void prepare(java.awt.geom.Area polygonArea){
+ private void prepare(java.awt.geom.Area polygonArea) {
extraDensityInfo = new EnhancedDensityMap(allDensities, polygonArea);
- if (!beQuiet){
+ if (!beQuiet) {
System.out.println("Highest node count in a single grid element is "
+ Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElement()));
- if (polygonArea != null)
- System.out.println("Highest node count in a single grid element within the bounding polygon is "
- + Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElementInPoly()));
+ if (polygonArea != null) {
+ System.out.println("Highest node count in a single grid element within the bounding polygon is "
+ + Utils.format(extraDensityInfo.getMaxNodesInDensityMapGridElementInPoly()));
+ }
}
if (polygonArea != null)
trimTiles = true;
@@ -352,27 +358,27 @@ public class SplittableDensityArea {
}
/**
- * Check if the solution should be stored in the map of partial good solutions
+ * Check if the solution should be stored in the map of partial good solutions
+ *
* @param tile the tile for which the solution was found
- * @param sol the solution for the tile
+ * @param sol the solution for the tile
*/
- private void checkIfGood(Tile tile, Solution sol){
- if (sol.isNice() == false || sol.getTiles().size() < 2)
- return;
- if (sol.getWorstMinNodes() > (goodRatio * maxNodes)){
+ private void checkIfGood(Tile tile, Solution sol) {
+ if (sol.isNice() && sol.getTiles().size() >= 2 && sol.getWorstMinNodes() > (goodRatio * maxNodes)) {
Solution good = sol.copy();
- // add new or replace worse solution
- goodSolutions.compute(tile, (k,v) -> v == null || v.getWorstMinNodes() < good.getWorstMinNodes() ? good : v);
+ // add new or replace worse solution
+ goodSolutions.compute(tile,
+ (k, v) -> v == null || v.getWorstMinNodes() < good.getWorstMinNodes() ? good : v);
}
-
}
/**
- * Remove entries from the map of partial good solutions which
- * cannot help to improve the best solution.
+ * Remove entries from the map of partial good solutions which cannot help to
+ * improve the best solution.
+ *
* @param best the best known solution
*/
- private void filterGoodSolutions(Solution best){
+ private void filterGoodSolutions(Solution best) {
if (best == null || best.isEmpty())
return;
goodSolutions.entrySet().removeIf(entry -> entry.getValue().getWorstMinNodes() <= best.getWorstMinNodes());
@@ -380,29 +386,30 @@ public class SplittableDensityArea {
}
/**
- * Search a solution for the given tile in the map of partial good solutions
+ * Search a solution for the given tile in the map of partial good solutions
+ *
* @param tile the tile to split
* @return a copy of the best known solution or null
*/
- private Solution searchGoodSolutions(Tile tile){
+ private Solution searchGoodSolutions(Tile tile) {
Solution sol = goodSolutions.get(tile);
- if (sol != null){
+ if (sol != null) {
if (sol.getWorstMinNodes() < minNodes)
return null;
sol = sol.copy();
}
return sol;
}
-
-
+
/**
* Try to find empty areas. This will fail if the empty area is enclosed by a
* non-empty area.
- * @param depth recursion depth
- * @param tile the tile that might contain an empty area
+ *
+ * @param depth recursion depth
+ * @param tile the tile that might contain an empty area
* @param splitHoriz true: search horizontal, else vertical
- * @return a list containing one or more tiles, cut from the original tile, or
- * just the original tile
+ * @return a list containing one or more tiles, cut from the original tile, or
+ * just the original tile
*/
private ArrayList<Tile> checkForEmptyClusters(int depth, final Tile tile, boolean splitHoriz) {
java.awt.geom.Area area = new java.awt.geom.Area(tile);
@@ -410,17 +417,20 @@ public class SplittableDensityArea {
int countEmpty = 0;
long countLastPart = 0;
long countRemaining = tile.getCount();
- long maxEmpty = Utils.toMapUnit(30) / (1 << shift);
- if (splitHoriz){
- for (int i = 0; i < tile.width; i++){
+ int maxEmpty = Utils.toMapUnit(30) / (1 << shift);
+ int minEmpty = Utils.toMapUnit(10) / (1 << shift);
+ if (splitHoriz) {
+ for (int i = 0; i < tile.width; i++) {
long count = tile.getColSum(i);
- if (count == 0){
+ if (count == 0) {
if (firstEmpty < 0)
firstEmpty = i;
countEmpty++;
} else {
- if (countEmpty > maxEmpty || (countEmpty > 10 && countLastPart > maxNodes/3 && countRemaining > maxNodes/3)){
- java.awt.geom.Area empty = new java.awt.geom.Area(new Rectangle(firstEmpty,tile.y,countEmpty,tile.height));
+ if (countEmpty > maxEmpty
+ || (countEmpty > minEmpty && countLastPart > maxNodes / 3 && countRemaining > maxNodes / 3)) {
+ java.awt.geom.Area empty = new java.awt.geom.Area(
+ new Rectangle(firstEmpty, tile.y, countEmpty, tile.height));
area.subtract(empty);
countLastPart = 0;
}
@@ -431,15 +441,17 @@ public class SplittableDensityArea {
}
}
} else {
- for (int i = 0; i < tile.height; i++){
+ for (int i = 0; i < tile.height; i++) {
long count = tile.getRowSum(i);
- if (count == 0){
+ if (count == 0) {
if (firstEmpty < 0)
firstEmpty = i;
countEmpty++;
} else {
- if (countEmpty > maxEmpty || (countEmpty > 10 && countLastPart > maxNodes/3 && countRemaining > maxNodes/3)){
- java.awt.geom.Area empty = new java.awt.geom.Area(new Rectangle(tile.x,firstEmpty,tile.width,countEmpty));
+ if (countEmpty > maxEmpty
+ || (countEmpty > minEmpty && countLastPart > maxNodes / 3 && countRemaining > maxNodes / 3)) {
+ java.awt.geom.Area empty = new java.awt.geom.Area(
+ new Rectangle(tile.x, firstEmpty, tile.width, countEmpty));
area.subtract(empty);
countLastPart = 0;
}
@@ -451,19 +463,19 @@ public class SplittableDensityArea {
}
}
ArrayList<Tile> clusters = new ArrayList<>();
- if (depth == 0 && area.isSingular()){
- // try also the other split axis
- clusters.addAll(checkForEmptyClusters(depth + 1, tile.trim(), !splitHoriz ));
+ if (depth == 0 && area.isSingular()) {
+ // try also the other split axis
+ clusters.addAll(checkForEmptyClusters(depth + 1, tile.trim(), !splitHoriz));
} else {
- if (area.isSingular()){
+ if (area.isSingular()) {
clusters.add(tile.trim());
} else {
List<List<Point>> shapes = Utils.areaToShapes(area);
- for (List<Point> shape: shapes){
+ for (List<Point> shape : shapes) {
java.awt.geom.Area part = Utils.shapeToArea(shape);
Tile t = new Tile(extraDensityInfo, part.getBounds());
if (t.getCount() > 0)
- clusters.addAll(checkForEmptyClusters(depth + 1, t.trim(), !splitHoriz ));
+ clusters.addAll(checkForEmptyClusters(depth + 1, t.trim(), !splitHoriz));
}
}
}
@@ -472,30 +484,33 @@ public class SplittableDensityArea {
/**
* Split, handling a polygon that may contain multiple distinct areas.
+ *
* @param polygonArea
* @return a list of areas that cover the polygon
*/
private List<Area> splitPolygon(final java.awt.geom.Area polygonArea) {
List<Area> result = new ArrayList<>();
List<List<Point>> shapes = Utils.areaToShapes(polygonArea);
- for (int i = 0; i < shapes.size(); i++){
+ for (int i = 0; i < shapes.size(); i++) {
List<Point> shape = shapes.get(i);
- if (Utils.clockwise(shape) == false)
+ if (!Utils.clockwise(shape))
continue;
java.awt.geom.Area shapeArea = Utils.shapeToArea(shape);
Rectangle rShape = shapeArea.getBounds();
- if (shape.size() > MAX_SINGLE_POLYGON_VERTICES){
+ if (shape.size() > MAX_SINGLE_POLYGON_VERTICES) {
shapeArea = new java.awt.geom.Area(rShape);
- System.out.println("Warning: shape is too complex, using rectangle " + rShape+ " instead");
+ System.out.println("Warning: shape is too complex, using rectangle " + rShape + " instead");
}
- Area shapeBounds = new Area(rShape.y, rShape.x,(int)rShape.getMaxY(), (int)rShape.getMaxX());
- int resolution = 24-allDensities.getShift();
- shapeBounds = RoundingUtils.round(shapeBounds, resolution);
- SplittableDensityArea splittableArea = new SplittableDensityArea(allDensities.subset(shapeBounds), startSearchLimit);
+ Area shapeBounds = new Area(rShape.y, rShape.x, (int) rShape.getMaxY(), (int) rShape.getMaxX());
+ int resolution = 24 - allDensities.getShift();
+ shapeBounds = RoundingUtils.round(shapeBounds, resolution);
+ SplittableDensityArea splittableArea = new SplittableDensityArea(allDensities.subset(shapeBounds),
+ startSearchLimit);
splittableArea.setMaxNodes(maxNodes);
- if (splittableArea.hasData() == false){
- System.out.println("Warning: a part of the bounding polygon would be empty and is ignored:" + shapeBounds);
- //result.add(shapeBounds);
+ if (!splittableArea.hasData()) {
+ System.out.println(
+ "Warning: a part of the bounding polygon would be empty and is ignored:" + shapeBounds);
+ // result.add(shapeBounds);
continue;
}
List<Area> partResult = splittableArea.split(shapeArea);
@@ -504,97 +519,102 @@ public class SplittableDensityArea {
}
return result;
}
-
/**
- * Split the given tile using the given (singular) polygon area. The routine splits the polygon into parts
- * and calls itself recursively for each part that is not rectangular.
- * @param depth recursion depth
- * @param tile the tile to split
+ * Split the given tile using the given (singular) polygon area. The routine
+ * splits the polygon into parts and calls itself recursively for each part that
+ * is not rectangular.
+ *
+ * @param depth recursion depth
+ * @param tile the tile to split
* @param rasteredPolygonArea an area describing a rectilinear shape
- * @return a solution (maybe empty)
+ * @return a solution (maybe empty), or null if rasteredPolygon is not rectangular
*/
private Solution findSolutionWithSinglePolygon(int depth, final Tile tile, java.awt.geom.Area rasteredPolygonArea) {
- assert rasteredPolygonArea.isSingular();
- if (rasteredPolygonArea.isRectangular()){
+ if (!rasteredPolygonArea.isSingular()) {
+ return null;
+ }
+ if (rasteredPolygonArea.isRectangular()) {
Tile part = new Tile(extraDensityInfo, rasteredPolygonArea.getBounds());
return solveRectangularArea(part);
}
List<List<Point>> shapes = Utils.areaToShapes(rasteredPolygonArea);
List<Point> shape = shapes.get(0);
-
- if (shape.size() > MAX_SINGLE_POLYGON_VERTICES){
+
+ if (shape.size() > MAX_SINGLE_POLYGON_VERTICES) {
Tile part = new Tile(extraDensityInfo, rasteredPolygonArea.getBounds());
System.out.println("Warning: shape is too complex, using rectangle " + part + " instead");
return solveRectangularArea(part);
}
-
+
Rectangle pBounds = rasteredPolygonArea.getBounds();
int lastPoint = shape.size() - 1;
if (shape.get(0).equals(shape.get(lastPoint)))
--lastPoint;
- for (int i = 0; i <= lastPoint; i++){
+ for (int i = 0; i <= lastPoint; i++) {
Point point = shape.get(i);
if (i > 0 && point.equals(shape.get(0)))
continue;
int cutX = point.x;
int cutY = point.y;
- Solution part0Sol = null,part1Sol = null;
- for (int axis = 0; axis < 2; axis++){
- Rectangle r1,r2;
- if (axis == AXIS_HOR){
- r1 = new Rectangle(pBounds.x,pBounds.y,cutX-pBounds.x,pBounds.height);
- r2 = new Rectangle(cutX,pBounds.y,(int)(pBounds.getMaxX()-cutX),pBounds.height);
+ Solution part0Sol = null, part1Sol = null;
+ for (int axis = 0; axis < 2; axis++) {
+ Rectangle r1, r2;
+ if (axis == AXIS_HOR) {
+ r1 = new Rectangle(pBounds.x, pBounds.y, cutX - pBounds.x, pBounds.height);
+ r2 = new Rectangle(cutX, pBounds.y, (int) (pBounds.getMaxX() - cutX), pBounds.height);
} else {
- r1 = new Rectangle(pBounds.x,pBounds.y,pBounds.width,cutY-pBounds.y);
- r2 = new Rectangle(pBounds.x,cutY,pBounds.width,(int)(pBounds.getMaxY()-cutY));
+ r1 = new Rectangle(pBounds.x, pBounds.y, pBounds.width, cutY - pBounds.y);
+ r2 = new Rectangle(pBounds.x, cutY, pBounds.width, (int) (pBounds.getMaxY() - cutY));
}
- if (r1.width * r1.height> r2.width * r2.height){
+ if (r1.width * r1.height > r2.width * r2.height) {
Rectangle help = r1;
r1 = r2;
r2 = help;
}
- if (r1.isEmpty() == false && r2.isEmpty() == false){
+ if (!r1.isEmpty() && !r2.isEmpty()) {
java.awt.geom.Area area = new java.awt.geom.Area(r1);
area.intersect(rasteredPolygonArea);
-
- part0Sol = findSolutionWithSinglePolygon(depth+1, tile, area);
- if (part0Sol != null && part0Sol.isEmpty() == false){
+
+ part0Sol = findSolutionWithSinglePolygon(depth + 1, tile, area);
+ if (part0Sol != null && !part0Sol.isEmpty()) {
area = new java.awt.geom.Area(r2);
area.intersect(rasteredPolygonArea);
- part1Sol = findSolutionWithSinglePolygon(depth+1, tile, area);
- if (part1Sol != null && part1Sol.isEmpty() == false)
+ part1Sol = findSolutionWithSinglePolygon(depth + 1, tile, area);
+ if (part1Sol != null && !part1Sol.isEmpty())
break;
}
}
}
- if (part1Sol != null){
+ if (part0Sol != null && part1Sol != null) {
part0Sol.merge(part1Sol);
return part0Sol;
}
}
return new Solution(maxNodes);
}
-
+
/**
- * Try to split the tile into nice parts recursively.
+ * Try to split the tile into nice parts recursively.
+ *
* @param depth the recursion depth
- * @param tile the tile to be split
- * @return a solution instance or null
+ * @param tile the tile to be split
+ * @param smiParent meta info for parent tile
+ * @return a solution instance or null
*/
- private Solution findSolution(int depth, final Tile tile, Tile parent, TileMetaInfo smiParent){
+ private Solution findSolution(int depth, final Tile tile, Tile parent, TileMetaInfo smiParent) {
boolean addAndReturn = false;
- if (tile.getCount() == 0){
- if (!allowEmptyPart){
- hasEmptyPart = true;
+ if (tile.getCount() == 0) {
+ if (!allowEmptyPart) {
+ hasEmptyPart = true;
return null;
}
- if (tile.width * tile.height <= 4)
+ if (tile.width * tile.height <= 4)
return null;
return new Solution(maxNodes); // allow empty part of the world
} else if (tile.getCount() > maxNodes && tile.width == 1 && tile.height == 1) {
- addAndReturn = true; // can't split further
+ addAndReturn = true; // can't split further
} else if (tile.getCount() < minNodes && depth == 0) {
addAndReturn = true; // nothing to do
} else if (tile.getCount() < minNodes) {
@@ -603,59 +623,56 @@ public class SplittableDensityArea {
double ratio = tile.getAspectRatio();
if (ratio < 1.0)
ratio = 1.0 / ratio;
- if (ratio < maxAspectRatio){
+ if (ratio <= maxAspectRatio) {
if (ignoreSize || maxNodes >= LARGE_MAX_NODES || checkSize(tile))
addAndReturn = true;
}
} else if (tile.width < 2 && tile.height < 2) {
return null;
- }
- if (tile.outsidePolygon()){
- return new Solution(maxNodes);
}
- if (addAndReturn){
+ if (addAndReturn) {
double outsidePolygonRatio = tile.calcOutsidePolygonRatio();
- if (outsidePolygonRatio > maxOutsidePolygonRatio ){
+ if (outsidePolygonRatio > maxOutsidePolygonRatio) {
return null;
}
Solution solution = new Solution(maxNodes);
- solution.add(tile); // can't split further
+ solution.add(tile); // can't split further
return solution;
}
- if (tile.getCount() < minNodes * 2){
+ if (tile.getCount() < minNodes * 2) {
return null;
}
Solution cached = searchGoodSolutions(tile);
- if (cached != null){
+ if (cached != null) {
return cached;
- }
+ }
// we have to split the tile
Integer alreadyDone = null;
- if (countBad == 0 && incomplete.size() > 0){
+ if (countBad == 0 && incomplete.size() > 0) {
alreadyDone = incomplete.remove(tile);
if (alreadyDone == null)
incomplete.clear(); // rest is not useful
}
-
- if (alreadyDone == null && depth > 0 && tile.width * tile.height > 100){
+
+ if (alreadyDone == null && depth > 0 && tile.width * tile.height > 100) {
if (knownBad.contains(tile))
return null;
}
- // copy the existing density info from parent
+ // copy the existing density info from parent
// typically, at least one half can be re-used
TileMetaInfo smi = new TileMetaInfo(tile, parent, smiParent);
-
+
// we have to split the tile
- int axis = (tile.getAspectRatio() >= 1.0) ? AXIS_HOR:AXIS_VERT;
-
+ int axis = (tile.getAspectRatio() >= 1.0) ? AXIS_HOR : AXIS_VERT;
+
IntArrayList todoList = generateTestCases(axis, tile, smi);
int countAxis = 0;
int usedTestPos = 0;
int countDone = 0;
Solution bestSol = null;
- while(true){
- if (usedTestPos >= todoList.size()){
+ while (true) {
+ if (usedTestPos >= todoList.size()) {
countAxis++;
if (countAxis > 1)
break;
@@ -665,13 +682,14 @@ public class SplittableDensityArea {
continue;
}
countDone++;
- if (alreadyDone != null && countDone <= alreadyDone.intValue()){
+ if (alreadyDone != null && countDone <= alreadyDone.intValue()) {
+ usedTestPos++; // we already checked this split before
continue;
}
int splitPos = todoList.getInt(usedTestPos++);
- // create the two parts of the tile
+ // create the two parts of the tile
boolean ok = false;
- if (axis == AXIS_HOR){
+ if (axis == AXIS_HOR) {
ok = tile.splitHoriz(splitPos, smi);
} else {
ok = tile.splitVert(splitPos, smi);
@@ -680,28 +698,30 @@ public class SplittableDensityArea {
continue;
Tile[] parts = smi.getParts();
- if (trimTiles){
+ if (trimTiles) {
parts[0] = parts[0].trim();
parts[1] = parts[1].trim();
}
- if (parts[0].getCount() > parts[1].getCount()){
+ if (parts[0].getCount() > parts[1].getCount()) {
// first try the less populated part
Tile help = parts[0];
parts[0] = parts[1];
parts[1] = help;
}
- Solution [] sols = new Solution[2];
+ Solution[] sols = new Solution[2];
int countOK = 0;
- for (int i = 0; i < 2; i++){
+ for (int i = 0; i < 2; i++) {
+ if (i == 0 && alreadyDone != null)
+ continue;
// depth first recursive search
// long t1 = System.currentTimeMillis();
sols[i] = findSolution(depth + 1, parts[i], tile, smi);
// long dt = System.currentTimeMillis() - t1;
// if (dt > 100 && tile.getCount() < 8 * maxNodes)
// System.out.println("took " + dt + " ms to find " + sols[i] + " for "+ parts[i]);
- if (sols[i] == null){
+ if (sols[i] == null) {
countBad++;
-// if (countBad >= searchLimit){
+// if (countBad >= searchLimit) {
// if (countBad == searchLimit)
// System.out.println("giving up at depth " + depth + " with currX = " + currX + " and currY = "+ currY + " at tile " + tile + " bad: " + saveCountBad );
// else
@@ -712,48 +732,50 @@ public class SplittableDensityArea {
checkIfGood(parts[i], sols[i]);
countOK++;
}
- if (countOK == 2){
+ if (countOK == 2) {
Solution sol = sols[0];
sol.merge(sols[1]);
bestSol = sol;
break; // we found a valid split and searched the direct neighbours
}
- if (countBad >= searchLimit){
- incomplete.put(tile, countDone-1);
+ if (countBad >= searchLimit) {
+ incomplete.put(tile, countDone - 1);
break;
}
}
-
+
smi.propagateToParent(smiParent, tile, parent);
-
- if (bestSol == null && countBad < searchLimit && depth > 0 && tile.width * tile.height > 100){
+
+ if (bestSol == null && countBad < searchLimit && depth > 0 && tile.width * tile.height > 100) {
knownBad.add(tile);
}
return bestSol;
}
-
+
private boolean checkSize(Tile tile) {
- return tile.height <= maxTileHeight && tile.width <= maxTileWidth;
+ return tile.height <= maxTileHeight && tile.width <= maxTileWidth;
}
-
+
/**
- * Get a first solution and search for better ones until
- * either a nice solution is found or no improvement was
- * found.
+ * Get a first solution and search for better ones until either a nice solution
+ * is found or no improvement was found.
+ *
* @param startTile the tile to split
* @return a solution (maybe be empty)
*/
- private Solution solveRectangularArea(Tile startTile){
- // start values for optimization process: we make little steps towards a good solution
-// spread = 7;
- if (startTile.getCount() == 0)
- return new Solution(maxNodes);
+ private Solution solveRectangularArea(Tile startTile) {
+ // start values for optimization process: we make little steps towards a good
+ // solution
+ if (startTile.getCount() == 0)
+ return new Solution(maxNodes);
searchLimit = startSearchLimit;
- minNodes = Math.max(Math.min((long)(0.05 * maxNodes), extraDensityInfo.getNodeCount()), 1);
-
- if (extraDensityInfo.getNodeCount() / maxNodes < 4){
+
+ final long startMinNodes = Math.max(Math.min((long) (0.05 * maxNodes), extraDensityInfo.getNodeCount()), 1);
+ minNodes = startMinNodes;
+
+ if (extraDensityInfo.getNodeCount() / maxNodes < 4) {
maxAspectRatio = 32;
- } else {
+ } else {
maxAspectRatio = startTile.getAspectRatio();
if (maxAspectRatio < 1)
maxAspectRatio = 1 / maxAspectRatio;
@@ -763,51 +785,58 @@ public class SplittableDensityArea {
goodSolutions = new HashMap<>(GOOD_SOL_INIT_SIZE);
goodRatio = 0.5;
TileMetaInfo smiStart = new TileMetaInfo(startTile, null, null);
- if (startTile.getCount() < 300 * maxNodes && (checkSize(startTile) || startTile.getCount() < 10 * maxNodes) ){
+ if (!ignoreSize && startTile.getCount() < 300 * maxNodes && (checkSize(startTile) || startTile.getCount() < 10 * maxNodes)) {
searchAll = true;
}
-
+ boolean algorithmnWasSwitched = false;
+
if (!beQuiet)
System.out.println("Trying to find nice split for " + startTile);
Solution bestSolution = new Solution(maxNodes);
- Solution prevBest = new Solution(maxNodes);
long t1 = System.currentTimeMillis();
incomplete = new LinkedHashMap<>();
resetCaches();
- for (int numLoops = 0; numLoops < MAX_LOOPS; numLoops++){
- double saveMaxAspectRatio = maxAspectRatio;
+ boolean clearIncomplete = false;
+ Solution firstSolution = new Solution(maxNodes);
+ for (int numLoops = 0; numLoops < MAX_LOOPS; numLoops++) {
+ if (clearIncomplete)
+ incomplete.clear();
+ clearIncomplete = true;
+ double saveMaxAspectRatio = maxAspectRatio;
long saveMinNodes = minNodes;
boolean foundBetter = false;
Solution solution = null;
countBad = 0;
- if (!beQuiet){
- System.out.println("searching for split with min-nodes " + minNodes + ", learned " + goodSolutions.size() + " good partial solutions");
+ if (!beQuiet) {
+ System.out.println("searching for split with min-nodes " + minNodes + ", learned "
+ + goodSolutions.size() + " good partial solutions");
}
smiStart.setMinNodes(minNodes);
solution = findSolution(0, startTile, startTile, smiStart);
- if (solution != null){
+ if (solution != null) {
foundBetter = bestSolution.compareTo(solution) > 0;
- if (foundBetter){
- prevBest = bestSolution;
+ if (foundBetter) {
+ Solution prevBest = bestSolution;
bestSolution = solution;
-
- System.out.println("Best solution until now: " + bestSolution.toString() + ", elapsed search time: " + (System.currentTimeMillis() - t1) / 1000 + " s");
+ if (bestSolution.compareTo(firstSolution) < 0) {
+ System.out.println("Best solution until now: " + bestSolution.toString() + ", elapsed search time: "
+ + (System.currentTimeMillis() - t1) / 1000 + " s");
+ }
filterGoodSolutions(bestSolution);
// change criteria to find a better(nicer) result
double factor = 1.10;
- if (prevBest.isEmpty() == false && prevBest.isNice() )
- factor = Math.min(1.30,(double)bestSolution.getWorstMinNodes() / prevBest.getWorstMinNodes());
- minNodes = Math.max(maxNodes /3, (long) (bestSolution.getWorstMinNodes() * factor));
+ if (!prevBest.isEmpty() && prevBest.isNice())
+ factor = Math.min(1.30, (double) bestSolution.getWorstMinNodes() / prevBest.getWorstMinNodes());
+ minNodes = Math.max(maxNodes / 3, (long) (bestSolution.getWorstMinNodes() * factor));
}
- if (bestSolution.size() == 1){
+ if (bestSolution.size() == 1) {
if (!beQuiet)
System.out.println("This can't be improved.");
break;
}
- }
- else {
- if (bestSolution.isEmpty() == false){
- if (minNodes > bestSolution.getWorstMinNodes() + 1){
+ } else {
+ if (!bestSolution.isEmpty()) {
+ if (minNodes > bestSolution.getWorstMinNodes() + 1) {
// reduce minNodes
minNodes = (bestSolution.getWorstMinNodes() + minNodes) / 2;
if (minNodes < bestSolution.getWorstMinNodes() * 1.001)
@@ -815,21 +844,23 @@ public class SplittableDensityArea {
}
}
}
- maxAspectRatio = Math.max(bestSolution.getWorstAspectRatio()/2, NICE_MAX_ASPECT_RATIO);
- maxAspectRatio = Math.min(32,maxAspectRatio);
- if (bestSolution.isEmpty() == false && bestSolution.getWorstMinNodes() > VERY_NICE_FILL_RATIO * maxNodes)
+ maxAspectRatio = Math.max(bestSolution.getWorstAspectRatio() / 2, NICE_MAX_ASPECT_RATIO);
+ maxAspectRatio = Math.min(32, maxAspectRatio);
+ if (!bestSolution.isEmpty() && bestSolution.getWorstMinNodes() > VERY_NICE_FILL_RATIO * maxNodes)
break;
if (minNodes > VERY_NICE_FILL_RATIO * maxNodes)
minNodes = (long) (VERY_NICE_FILL_RATIO * maxNodes);
- if (saveMaxAspectRatio == maxAspectRatio && saveMinNodes == minNodes){
- if (bestSolution.isEmpty() || bestSolution.getWorstMinNodes() < 0.5 * maxNodes){
- if (countBad > searchLimit && searchLimit < 5_000_000){
+ if (saveMaxAspectRatio == maxAspectRatio && saveMinNodes == minNodes) {
+ // no improvement found
+ if (bestSolution.isEmpty() || bestSolution.getWorstMinNodes() < 0.5 * maxNodes) {
+ if (countBad > searchLimit && searchLimit < 5_000_000) {
searchLimit *= 2;
+ clearIncomplete = false;
resetCaches();
System.out.println("No good solution found, duplicated search-limit to " + searchLimit);
continue;
}
- if (bestSolution.isEmpty() && minNodes > 1){
+ if (bestSolution.isEmpty() && minNodes > 1) {
minNodes = 1;
resetCaches();
searchLimit = startSearchLimit;
@@ -839,54 +870,65 @@ public class SplittableDensityArea {
// inform user about possible better options?
double ratio = (double) highestCount / maxNodes;
if (ratio > 4)
- System.err.printf("max-nodes value %d is far below highest node count %d in single grid element, consider using a higher resolution.\n", maxNodes , highestCount);
+ System.err.printf(
+ "max-nodes value %d is far below highest node count %d in single grid element, consider using a higher resolution.%n",
+ maxNodes, highestCount);
else if (ratio > 1)
- System.err.printf("max-nodes value %d is below highest node count %d in single grid element, consider using a higher resolution.\n", maxNodes , highestCount);
+ System.err.printf(
+ "max-nodes value %d is below highest node count %d in single grid element, consider using a higher resolution.%n",
+ maxNodes, highestCount);
else if (ratio < 0.25)
- System.err.printf("max-nodes value %d is far above highest node count %d in single grid element, consider using a lower resolution.\n", maxNodes , highestCount);
-
+ System.err.printf(
+ "max-nodes value %d is far above highest node count %d in single grid element, consider using a lower resolution.%n",
+ maxNodes, highestCount);
+
continue;
}
- if (searchAll){
- searchAll = false;
- if (bestSolution.isEmpty() == false)
- minNodes = bestSolution.getWorstMinNodes() + 1;
- else
- minNodes = maxNodes / 100;
- System.out.println("Still no good solution found, trying alternate algorithm");
+ if (!algorithmnWasSwitched) {
+ System.out.println("Still no good solution found, trying alternative algorithm");
+ firstSolution = bestSolution;
+ bestSolution = new Solution(maxNodes);
+ minNodes = startMinNodes;
+ searchLimit = startSearchLimit;
+ searchAll = !searchAll;
+ algorithmnWasSwitched = true;
continue;
}
- }
+ }
break;
}
-
- }
+ }
+ if (bestSolution.compareTo(firstSolution) > 0)
+ bestSolution = firstSolution;
if (!beQuiet)
printFinishMsg(bestSolution);
return bestSolution;
}
- private void resetCaches(){
+ private void resetCaches() {
knownBad = new HashSet<>(50_000);
}
-
- private void printFinishMsg(Solution solution){
- if (!beQuiet){
- if (solution.isEmpty() == false){
+
+ private void printFinishMsg(Solution solution) {
+ if (!beQuiet) {
+ if (!solution.isEmpty()) {
if (solution.getWorstMinNodes() > VERY_NICE_FILL_RATIO * maxNodes && solution.isNice())
- System.out.println("Solution is very nice. No need to search for a better solution: " + solution.toString());
- else
- System.out.println("Solution is " + (solution.isNice() ? "":"not ") + "nice. Can't find a better solution with search-limit " + searchLimit + ": " + solution.toString());
+ System.out.println(
+ "Solution is very nice. No need to search for a better solution: " + solution.toString());
+ else
+ System.out.println("Solution is " + (solution.isNice() ? "" : "not ")
+ + "nice. Can't find a better solution with search-limit " + searchLimit + ": "
+ + solution.toString());
}
}
- return;
}
-
+
/**
- * Convert the list of Tile instances of a solution to Area instances, report some
- * statistics.
- * @param sol the solution
- * @param polygonArea
+ * Convert the list of Tile instances of a solution to Area instances, report
+ * some statistics.
+ *
+ * @param sol the solution
+ * @param polygonArea the split polygon
*
* @return list of areas
*/
@@ -894,46 +936,46 @@ public class SplittableDensityArea {
List<Area> result = new ArrayList<>();
int minLat = getAllDensities().getBounds().getMinLat();
int minLon = getAllDensities().getBounds().getMinLong();
-
- if (polygonArea != null){
+
+ if (polygonArea != null) {
System.out.println("Trying to cut the areas so that they fit into the polygon ...");
} else {
if (trimShape)
sol.trimOuterTiles();
}
-
- boolean fits = true;
+
+ boolean fits = true;
for (Tile tile : sol.getTiles()) {
if (tile.getCount() == 0)
continue;
- if (tile.verify() == false)
+ if (!tile.verify())
throw new SplitFailedException("found invalid tile");
- Rectangle r = new Rectangle(minLon + (tile.x << shift), minLat + (tile.y << shift),
- tile.width << shift, tile.height << shift);
-
- if (polygonArea != null){
+ Rectangle r = new Rectangle(minLon + (tile.x << shift), minLat + (tile.y << shift), tile.width << shift,
+ tile.height << shift);
+
+ if (polygonArea != null) {
java.awt.geom.Area cutArea = new java.awt.geom.Area(r);
cutArea.intersect(polygonArea);
- if (cutArea.isEmpty() == false && cutArea.isRectangular() )
+ if (!cutArea.isEmpty() && cutArea.isRectangular()) {
r = cutArea.getBounds();
- else {
+ } else {
fits = false;
}
}
- Area area = new Area(r.y,r.x,(int)r.getMaxY(),(int)r.getMaxX());
- if (!beQuiet){
+ Area area = new Area(r.y, r.x, (int) r.getMaxY(), (int) r.getMaxX());
+ if (!beQuiet) {
String note;
if (tile.getCount() > maxNodes)
note = " but is already at the minimum size so can't be split further";
else
note = "";
long percentage = 100 * tile.getCount() / maxNodes;
- System.out.println("Area " + currMapId++ + " covers " + area
- + " and contains " + tile.getCount() + " nodes (" + percentage + " %)" + note);
+ System.out.println("Area " + currMapId++ + " covers " + area + " and contains " + tile.getCount()
+ + " nodes (" + percentage + " %)" + note);
}
result.add(area);
}
- if (fits == false){
+ if (!fits) {
System.out.println("One or more areas do not exactly fit into the bounding polygon");
}
return result;
@@ -941,16 +983,16 @@ public class SplittableDensityArea {
}
/**
- * Generate a list of split positions.
- * This is essential: The shorter the list, the faster the search, so we try to
- * put only good candidates into the list.
- * @param axis
- * @param tile
- * @param smi
- * @return
+ * Generate a list of split positions. This is essential: The shorter the list,
+ * the faster the search, so we try to put only good candidates into the list.
+ *
+ * @param axis horizontal or vertical
+ * @param tile the tile
+ * @param smi the meta info
+ * @return list of split positions
*/
private IntArrayList generateTestCases(int axis, Tile tile, TileMetaInfo smi) {
- if (searchAll){
+ if (searchAll) {
return (axis == AXIS_HOR) ? tile.genXTests(smi) : tile.genYTests(smi);
}
double ratio = tile.getAspectRatio();
@@ -960,47 +1002,48 @@ public class SplittableDensityArea {
int start = (axis == AXIS_HOR) ? tile.findValidStartX(smi) : tile.findValidStartY(smi);
int end = (axis == AXIS_HOR) ? tile.findValidEndX(smi) : tile.findValidEndY(smi);
int range = end - start;
- if (range < 0){
+ if (range < 0) {
// can't split tile without having one part that has too few nodes
return tests;
}
- if (range > 1024 && (axis == AXIS_HOR && tile.width >= maxTileWidth || axis == AXIS_VERT && tile.height >= maxTileWidth)){
- // large tile, just split at a few valid positions
+ if (range > 1024 && (axis == AXIS_HOR && tile.width >= maxTileWidth
+ || axis == AXIS_VERT && tile.height >= maxTileHeight)) {
+ // large tile, just split at a few valid positions
for (int i = 5; i > 1; --i)
tests.add(start + range / i);
- }
- else if (tile.getCount() < maxNodes * 4 && range > 256){
+ } else if (tile.getCount() < maxNodes * 4 && range > 256) {
// large tile with rather few nodes, allow more tests
int step = (range) / 20;
for (int pos = start; pos <= end; pos += step)
tests.add(pos);
- }
- else if (tile.getCount() > maxNodes * 4){
+ } else if (tile.getCount() > maxNodes * 4) {
int step = range / 7; // 7 turned out to be a good value here
if (step < 1)
step = 1;
for (int pos = start; pos <= end; pos += step)
tests.add(pos);
} else {
- // this will be one of the last splits
+ // this will be one of the last splits
long nMax = tile.getCount() / minNodes;
if (nMax * minNodes < tile.getCount())
nMax++;
long nMin = tile.getCount() / maxNodes;
if (nMin * maxNodes < tile.getCount())
nMin++;
- if (nMin > 2 && nMin * maxNodes - minNodes < tile.getCount() && ratio > 0.125 && ratio < 8){
- // count is near (but below) a multiple of max-nodes, we have to test all candidates
- // to make sure that we don't miss a good split
+ if (nMin > 2 && nMin * maxNodes - minNodes < tile.getCount() && ratio > 0.125 && ratio < 8) {
+ // count is near (but below) a multiple of max-nodes, we have to test all
+ // candidates
+ // to make sure that we don't miss a good split
return (axis == AXIS_HOR) ? tile.genXTests(smi) : tile.genYTests(smi);
}
- if (nMax == 2 || nMin == 2){
+ if (nMax == 2 || nMin == 2) {
tests.add((axis == AXIS_HOR) ? tile.findHorizontalMiddle(smi) : tile.findVerticalMiddle(smi));
- int pos = (axis == AXIS_HOR) ? tile.findFirstXHigher(smi, minNodes) + 1 : tile.findFirstYHigher(smi, minNodes) + 1;
+ int pos = (axis == AXIS_HOR) ? tile.findFirstXHigher(smi, minNodes) + 1
+ : tile.findFirstYHigher(smi, minNodes) + 1;
if (tests.get(0) != pos)
tests.add(pos);
pos = (axis == AXIS_HOR) ? tile.findFirstXHigher(smi, maxNodes) : tile.findFirstYHigher(smi, maxNodes);
- if (tests.contains(pos) == false)
+ if (!tests.contains(pos))
tests.add(pos);
} else {
if (range == 0) {
@@ -1008,9 +1051,9 @@ public class SplittableDensityArea {
} else {
if (nMax != 3)
tests.add((axis == AXIS_HOR) ? tile.findHorizontalMiddle(smi) : tile.findVerticalMiddle(smi));
- if (tests.contains(start) == false)
+ if (!tests.contains(start))
tests.add(start);
- if (tests.contains(end) == false)
+ if (!tests.contains(end))
tests.add(end);
}
}
@@ -1018,5 +1061,3 @@ public class SplittableDensityArea {
return tests;
}
}
-
-
=====================================
src/uk/me/parabola/splitter/writer/AbstractOSMWriter.java
=====================================
@@ -36,7 +36,7 @@ public abstract class AbstractOSMWriter implements OSMWriter{
protected int versionMethod;
- public AbstractOSMWriter(Area bounds, File outputDir, int mapId, int extra) {
+ protected AbstractOSMWriter(Area bounds, File outputDir, int mapId, int extra) {
this.mapId = mapId;
this.bounds = bounds;
this.outputDir = outputDir;
@@ -61,21 +61,26 @@ public abstract class AbstractOSMWriter implements OSMWriter{
}
+ @Override
public Area getBounds() {
return bounds;
}
+ @Override
public Area getExtendedBounds() {
return extendedBounds;
}
+ @Override
public int getMapId(){
return mapId;
}
+ @Override
public Rectangle getBBox(){
return bbox;
}
+ @Override
public void write (Element element) throws IOException {
if (element instanceof Node) {
write((Node) element);
=====================================
src/uk/me/parabola/splitter/writer/BinaryMapWriter.java
=====================================
@@ -175,6 +175,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
private class NodeGroup extends Prim<Node> implements PrimGroupWriterInterface {
+ @Override
public Osmformat.PrimitiveGroup serialize() {
if (useDense)
return serializeDense();
@@ -185,7 +186,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
* Serialize all nodes in the 'dense' format.
*/
public Osmformat.PrimitiveGroup serializeDense() {
- if (contents.size() == 0) {
+ if (contents.isEmpty()) {
return null;
}
// System.out.format("%d Dense ",nodes.size());
@@ -238,7 +239,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
* Add to this PrimitiveBlock.
*/
public Osmformat.PrimitiveGroup serializeNonDense() {
- if (contents.size() == 0) {
+ if (contents.isEmpty()) {
return null;
}
// System.out.format("%d Nodes ",nodes.size());
@@ -269,8 +270,9 @@ public class BinaryMapWriter extends AbstractOSMWriter {
}
private class WayGroup extends Prim<Way> implements PrimGroupWriterInterface {
+ @Override
public Osmformat.PrimitiveGroup serialize() {
- if (contents.size() == 0) {
+ if (contents.isEmpty()) {
return null;
}
@@ -302,6 +304,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
}
private class RelationGroup extends Prim<Relation> implements PrimGroupWriterInterface {
+ @Override
public void addStringsToStringtable() {
StringTable stable = serializer.getStringTable();
super.addStringsToStringtable();
@@ -312,8 +315,9 @@ public class BinaryMapWriter extends AbstractOSMWriter {
}
}
+ @Override
public Osmformat.PrimitiveGroup serialize() {
- if (contents.size() == 0) {
+ if (contents.isEmpty()) {
return null;
}
@@ -432,7 +436,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
groups.add(relations);
relations = null;
} else {
- return; // No data. Is this an empty file?
+ // No data. Is this an empty file?
}
}
@@ -450,6 +454,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
super(bounds, outputDir, mapId, extra);
}
+ @Override
public void initForWrite() {
String filename = String.format(Locale.ROOT, "%08d.osm.pbf", mapId);
try {
@@ -496,6 +501,7 @@ public class BinaryMapWriter extends AbstractOSMWriter {
headerWritten = true;
}
+ @Override
public void finishWrite() {
try {
serializer.switchTypes();
@@ -507,14 +513,17 @@ public class BinaryMapWriter extends AbstractOSMWriter {
}
}
+ @Override
public void write(Node node) {
serializer.processor.process(node);
}
+ @Override
public void write(Way way) {
serializer.processor.process(way);
}
+ @Override
public void write(Relation relation) {
serializer.processor.process(relation);
}
=====================================
src/uk/me/parabola/splitter/writer/O5mMapWriter.java
=====================================
@@ -18,6 +18,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
@@ -35,7 +36,7 @@ import uk.me.parabola.splitter.Way;
/**
* Implements the needed methods to write the result in the o5m format.
- * The routines to are based on the osmconvert.c source from Markus Weber who allows
+ * The routines are based on the osmconvert.c source from Markus Weber who allows
* to copy them for any o5m IO, thanks a lot for that.
*
* @author GerdP
@@ -69,7 +70,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
private long lastNodeId;
private long lastWayId;
private long lastRelId;
- private long lastRef[];
+ private long[] lastRef;
private int lastLon,lastLat;
private int lastWrittenDatasetType = 0;
@@ -77,22 +78,22 @@ public class O5mMapWriter extends AbstractOSMWriter{
// index of last entered element in string table
private short stw__tabi= 0;
- // has table; elements point to matching strings in stw__tab[];
- // -1: no matching element;
+ // has table; elements point to matching strings in stw__tab[]
+ // -1: no matching element
private short[] stw__hashtab;
// for to chaining of string table rows which match
- // the same hash value; matching rows are chained in a loop;
- // if there is only one row matching, it will point to itself;
+ // the same hash value; matching rows are chained in a loop
+ // if there is only one row matching, it will point to itself
private short[] stw__tabprev;
private short[] stw__tabnext;
- // has value of this element as a link back to the hash table;
- // a -1 element indicates that the string table entry is not used;
+ // has value of this element as a link back to the hash table
+ // a -1 element indicates that the string table entry is not used
private short[] stw__tabhash;
private byte[] numberConversionBuf;
- private final static Map<String, byte[]> wellKnownTagKeys = new HashMap<>(60, 0.25f);
- private final static Map<String, byte[]> wellKnownTagVals = new HashMap<>(20, 0.25f);
+ private static final Map<String, byte[]> wellKnownTagKeys = new HashMap<>(60, 0.25f);
+ private static final Map<String, byte[]> wellKnownTagVals = new HashMap<>(20, 0.25f);
static {
try {
@@ -114,7 +115,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
"ele", "tiger:separated", "tiger:zip_right",
"yh:WIDTH", "place", "foot"
)) {
- wellKnownTagKeys.put(s, s.getBytes("UTF-8"));
+ wellKnownTagKeys.put(s, s.getBytes(StandardCharsets.UTF_8));
}
for (String s : Arrays.asList(
@@ -122,7 +123,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
"footway", "Bing", "PGS", "private", "stream", "service",
"house", "unclassified", "track", "traffic_signals","restaurant","entrance"
)) {
- wellKnownTagVals.put(s, s.getBytes("UTF-8"));
+ wellKnownTagVals.put(s, s.getBytes(StandardCharsets.UTF_8));
}
} catch (Exception e) {
// should not happen
@@ -150,17 +151,18 @@ public class O5mMapWriter extends AbstractOSMWriter{
stw_reset();
}
+ @Override
public void initForWrite() {
- // has table; elements point to matching strings in stw__tab[];
- // -1: no matching element;
+ // has table; elements point to matching strings in stw__tab[]
+ // -1: no matching element
stw__hashtab = new short[STW_HASH_TAB_MAX];
// for to chaining of string table rows which match
- // the same hash value; matching rows are chained in a loop;
- // if there is only one row matching, it will point to itself;
+ // the same hash value; matching rows are chained in a loop
+ // if there is only one row matching, it will point to itself
stw__tabprev = new short[STW__TAB_MAX];
stw__tabnext = new short[STW__TAB_MAX];
- // has value of this element as a link back to the hash table;
- // a -1 element indicates that the string table entry is not used;
+ // has value of this element as a link back to the hash table
+ // a -1 element indicates that the string table entry is not used
stw__tabhash = new short[STW__TAB_MAX];
lastRef = new long[3];
numberConversionBuf = new byte[60];
@@ -202,6 +204,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
lastWrittenDatasetType = fileType;
}
+ @Override
public void finishWrite() {
try {
os.write(EOD_FLAG);
@@ -238,6 +241,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
writeDataset(NODE_DATASET,stream);
}
+ @Override
public void write(Way way) throws IOException {
if (lastWrittenDatasetType != WAY_DATASET){
reset();
@@ -260,6 +264,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
writeDataset(WAY_DATASET,stream);
}
+ @Override
public void write(Relation rel) throws IOException {
if (lastWrittenDatasetType != REL_DATASET){
reset();
@@ -325,12 +330,12 @@ public class O5mMapWriter extends AbstractOSMWriter{
int ref;
s1Bytes = wellKnownTagKeys.get(s1);
if (s1Bytes == null){
- s1Bytes = s1.getBytes("UTF-8");
+ s1Bytes = s1.getBytes(StandardCharsets.UTF_8);
}
if (s2 != null){
s2Bytes = wellKnownTagVals.get(s2);
if (s2Bytes == null){
- s2Bytes= s2.getBytes("UTF-8");
+ s2Bytes= s2.getBytes(StandardCharsets.UTF_8);
}
}
else
@@ -397,7 +402,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
i = stw__hashtab[hash];
if(i < 0) // no reference in hash table until now
stw__tabprev[stw__tabi] = stw__tabnext[stw__tabi] = stw__tabi;
- // self-link the new element;
+ // self-link the new element
else { // there is already a reference in hash table
// in-chain the new element
stw__tabnext[stw__tabi] = (short) i;
@@ -457,9 +462,8 @@ public class O5mMapWriter extends AbstractOSMWriter{
* @param s1
* @return hash value in the range 0..(STW__TAB_MAX-1)
* or -1 if the strings are longer than STW_TAB_STR_MAX bytes in total
- * @throws IOException
*/
- private int stw_hash(String s1, String s2) throws IOException{
+ private int stw_hash(String s1, String s2) {
int len = s1Bytes.length;
if (s2Bytes != null)
len += s2Bytes.length;
@@ -493,7 +497,7 @@ public class O5mMapWriter extends AbstractOSMWriter{
private int writeSignedNum(long num, OutputStream stream)throws IOException {
int cntBytes = 0;
// write a long as signed varying integer.
- // return: bytes written;
+ // return: bytes written
long u;
int part;
=====================================
src/uk/me/parabola/splitter/writer/OSMXMLWriter.java
=====================================
@@ -13,19 +13,13 @@
package uk.me.parabola.splitter.writer;
-import it.unimi.dsi.fastutil.longs.LongArrayList;
-import uk.me.parabola.splitter.Area;
-import uk.me.parabola.splitter.Element;
-import uk.me.parabola.splitter.Node;
-import uk.me.parabola.splitter.Relation;
-import uk.me.parabola.splitter.Utils;
-import uk.me.parabola.splitter.Way;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
+import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Iterator;
@@ -33,6 +27,14 @@ import java.util.List;
import java.util.Locale;
import java.util.zip.GZIPOutputStream;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import uk.me.parabola.splitter.Area;
+import uk.me.parabola.splitter.Element;
+import uk.me.parabola.splitter.Node;
+import uk.me.parabola.splitter.Relation;
+import uk.me.parabola.splitter.Utils;
+import uk.me.parabola.splitter.Way;
+
public class OSMXMLWriter extends AbstractOSMWriter{
private final DecimalFormat numberFormat = new DecimalFormat(
"0.#######;-0.#######",
@@ -45,13 +47,14 @@ public class OSMXMLWriter extends AbstractOSMWriter{
super(bounds, outputDir, mapId, extra);
}
+ @Override
public void initForWrite() {
String filename = String.format(Locale.ROOT, "%08d.osm.gz", mapId);
try {
FileOutputStream fos = new FileOutputStream(new File(outputDir, filename));
OutputStream zos = new GZIPOutputStream(fos);
- writer = new OutputStreamWriter(zos, "utf-8");
+ writer = new OutputStreamWriter(zos, StandardCharsets.UTF_8);
writeHeader();
} catch (IOException e) {
System.out.println("Could not open or write file header. Reason: " + e.getMessage());
@@ -76,6 +79,7 @@ public class OSMXMLWriter extends AbstractOSMWriter{
writeString("'/>\n");
}
+ @Override
public void finishWrite() {
try {
writeString("</osm>\n");
@@ -87,6 +91,7 @@ public class OSMXMLWriter extends AbstractOSMWriter{
}
}
+ @Override
public void write(Node node) throws IOException {
writeString("<node id='");
writeLong(node.getId());
@@ -106,6 +111,7 @@ public class OSMXMLWriter extends AbstractOSMWriter{
}
+ @Override
public void write(Way way) throws IOException {
writeString("<way id='");
writeLong(way.getId());
@@ -123,6 +129,7 @@ public class OSMXMLWriter extends AbstractOSMWriter{
writeString("</way>\n");
}
+ @Override
public void write(Relation rel) throws IOException {
writeString("<relation id='");
writeLong(rel.getId());
=====================================
test/uk/me/parabola/splitter/tools/SparseBitSetTest.java
=====================================
@@ -14,7 +14,7 @@
package uk.me.parabola.splitter.tools;
import static org.junit.Assert.assertEquals;
-import uk.me.parabola.splitter.tools.SparseBitSet;
+
import org.junit.Test;
/**
View it on GitLab: https://salsa.debian.org/debian-gis-team/mkgmap-splitter/-/compare/4d1fcbc060f1454040e26041c4773a4bbf901693...404b6870ea75e5e94caafeb19763b21adac1356c
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/mkgmap-splitter/-/compare/4d1fcbc060f1454040e26041c4773a4bbf901693...404b6870ea75e5e94caafeb19763b21adac1356c
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20210815/5669ff23/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list