Rev 4897 |
View as "text/plain" |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2012-2014.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package uk.me.parabola.mkgmap.sea.optional;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
import uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation;
import uk.me.parabola.mkgmap.reader.osm.Relation;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.util.Java2DConverter;
/**
* Merges the polygons for one precompiled sea tile.
* @author WanMil
*/
class PrecompSeaMerger
implements Runnable {
private final MergeData mergeData
;
private final CountDownLatch signal
;
private final BlockingQueue<Entry
<String,
List<Way
>>> saveQueue
;
private ExecutorService service
;
private static class MergeData
{
final Rectangle2D bounds
;
final BlockingQueue<Area> toMerge
;
final AtomicBoolean ready =
new AtomicBoolean(false);
Path2D.
Double tmpLandPath =
new Path2D.
Double();
Area landArea =
new Area();
private final String key
;
public MergeData
(Rectangle2D bounds,
String key
) {
this.
key = key
;
this.
bounds = bounds
;
toMerge =
new LinkedBlockingQueue<>();
}
public String getKey
() {
return key
;
}
}
public PrecompSeaMerger
(Rectangle2D bounds,
String key,
CountDownLatch signal,
BlockingQueue<Entry
<String,
List<Way
>>> saveQueue
) {
this.
mergeData =
new MergeData
(bounds, key
);
this.
signal = signal
;
this.
saveQueue = saveQueue
;
}
public MergeData getMergeData
() {
return mergeData
;
}
public BlockingQueue<Area> getQueue
() {
return mergeData.
toMerge;
}
public void signalInputComplete
() {
mergeData.
ready.
set(true);
}
public void setExecutorService
(ExecutorService service
) {
this.
service = service
;
}
public Rectangle2D getTileBounds
() {
return mergeData.
bounds;
}
private static List<Way
> convertToWays
(Area a,
String naturalTag
) {
List<List<Coord
>> pointLists = Java2DConverter.
areaToShapes(a
);
List<Way
> ways =
new ArrayList<>(pointLists.
size());
for (List<Coord
> points : pointLists
) {
Way w =
new Way
(FakeIdGenerator.
makeFakeId(), points
);
w.
addTag("natural", naturalTag
);
w.
setClosedInOSM(true);
ways.
add(w
);
}
return ways
;
}
public void run
() {
Area merge =
null;
try {
merge = mergeData.
toMerge.
poll(5,
TimeUnit.
MILLISECONDS);
} catch (InterruptedException exp
) {
exp.
printStackTrace();
}
int merges =
0;
while (merge
!=
null) {
Area landClipped =
new Area(mergeData.
bounds);
landClipped.
intersect(merge
);
mergeData.
tmpLandPath.
append(landClipped,
false);
merges++
;
if (merges
% 500 ==
0) {
// store each 500 polygons into a temporary area
// and merge them after that. That seems to be quicker
// than adding lots of very small areas to a highly
// scattered area
Area tmpLandArea =
new Area(mergeData.
tmpLandPath);
mergeData.
landArea.
add(tmpLandArea
);
mergeData.
tmpLandPath.
reset();
}
if (merges
% 500 ==
0) {
break;
}
merge = mergeData.
toMerge.
poll();
}
if (!mergeData.
ready.
get() ||
!mergeData.
toMerge.
isEmpty()) {
// repost the merge thread
service.
execute(this);
return;
}
if (mergeData.
landArea.
isEmpty())
mergeData.
landArea =
new Area(mergeData.
tmpLandPath);
else
mergeData.
landArea.
add(new Area(mergeData.
tmpLandPath));
mergeData.
tmpLandPath =
null;
// post processing //
// convert the land area to a list of ways
List<Way
> ways = convertToWays
(mergeData.
landArea,
"land");
if (ways.
isEmpty()) {
// no land in this tile => create a sea way only
ways.
addAll(convertToWays
(new Area(mergeData.
bounds),
"sea"));
} else {
Map<Long, Way
> wayMap =
new LinkedHashMap<>();
List<List<Coord
>> landParts = Java2DConverter.
areaToShapes(mergeData.
landArea);
Relation rel =
new GeneralRelation
(FakeIdGenerator.
makeFakeId());
for (List<Coord
> landPoints : landParts
) {
Way landWay =
new Way
(FakeIdGenerator.
makeFakeId(), landPoints
);
wayMap.
put(landWay.
getId(), landWay
);
rel.
addElement("inner", landWay
);
}
Way seaWay =
new Way
(FakeIdGenerator.
makeFakeId(), uk.
me.
parabola.
imgfmt.
app.
Area.
PLANET.
toCoords());
seaWay.
setClosedInOSM(true);
wayMap.
put(seaWay.
getId(), seaWay
);
rel.
addElement("outer", seaWay
);
// process the tile as sea multipolygon to create simple polygons only
MultiPolygonRelation mpr =
new MultiPolygonRelation
(rel, wayMap,
Java2DConverter.
createBbox(new Area(mergeData.
bounds)))
{
// do not calculate the area size => it is not required and adds
// a superfluous tag
@
Override
protected boolean needsAreaSizeTag
() {
return false;
}
@
Override
protected boolean noPartitioning
() {
return true;
}
};
mpr.
addTag("type",
"multipolygon");
mpr.
addTag("natural",
"sea");
mpr.
processElements();
for (Way w : wayMap.
values()) {
// process the polygon ways only
// the mp processing also creates line ways which must
// be ignored here
if (MultiPolygonRelation.
STYLE_FILTER_POLYGON.
equals(w.
getTag(MultiPolygonRelation.
STYLE_FILTER_TAG))
&& "sea".
equals(w.
getTag("natural"))) {
// ignore the land polygons - we already have them in our list
w.
deleteTag(MultiPolygonRelation.
STYLE_FILTER_TAG);
w.
deleteTag(MultiPolygonRelation.
TKM_MP_CREATED);
ways.
add(w
);
}
}
}
try {
// forward the ways to the queue of the saver thread
saveQueue.
put(new SimpleEntry
<>(mergeData.
getKey(), ways
));
} catch (InterruptedException exp
) {
exp.
printStackTrace();
}
// signal that this tile is finished
signal.
countDown();
}
}