/*
* Copyright (C) 2010, 2012.
*
* 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.reader.osm;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
import uk.me.parabola.mkgmap.reader.osm.xml.Osm5CoastDataSource;
import uk.me.parabola.util.EnhancedProperties;
public final class CoastlineFileLoader {
private static final Logger log = Logger
.getLogger(CoastlineFileLoader.class);
private static final List<Class<? extends LoadableMapDataSource>> coastFileLoaders;
static {
String[] sources = {
"uk.me.parabola.mkgmap.reader.osm.bin.OsmBinCoastDataSource",
// must be last as it is the default
"uk.me.parabola.mkgmap.reader.osm.xml.Osm5CoastDataSource", };
coastFileLoaders = new ArrayList<Class<? extends LoadableMapDataSource>>();
for (String source : sources) {
try {
@SuppressWarnings({ "unchecked" })
Class<? extends LoadableMapDataSource> c = (Class<? extends LoadableMapDataSource>) Class
.forName(source);
coastFileLoaders.add(c);
} catch (ClassNotFoundException e) {
// not available, try the rest
} catch (NoClassDefFoundError e) {
// not available, try the rest
}
}
}
private final Set<String> coastlineFiles;
private final Collection<CoastlineWay> coastlines = new ArrayList<CoastlineWay>();
private final AtomicBoolean coastlinesLoaded = new AtomicBoolean(false);
private final AtomicBoolean loadingStarted = new AtomicBoolean(false);
private final EnhancedProperties coastConfig;
private CoastlineFileLoader() {
this.coastlineFiles = new HashSet<String>();
this.coastConfig = new EnhancedProperties();
}
private static final CoastlineFileLoader loader = new CoastlineFileLoader();
public static synchronized CoastlineFileLoader getCoastlineLoader() {
return loader;
}
public synchronized void setCoastlineFiles(String[] coastlineFiles) {
this.coastlineFiles.addAll(Arrays.asList(coastlineFiles));
}
public void loadCoastlines() {
boolean loadInThisThread = loadingStarted.compareAndSet(false, true);
if (loadInThisThread) {
// load it
loadCoastlinesImpl();
} else {
log.info("Coastline loading performed by another thread");
}
}
private OsmMapDataSource loadFromFile(String name)
throws FileNotFoundException, FormatException {
OsmMapDataSource src = createMapReader(name);
src.config(getConfig());
log.info("Started loading coastlines from", name);
src.load(name);
log.info("Finished loading coastlines from", name);
return src;
}
public static OsmMapDataSource createMapReader(String name) {
for (Class<? extends LoadableMapDataSource> loader : coastFileLoaders) {
try {
LoadableMapDataSource src = loader.newInstance();
if (name != null && src instanceof OsmMapDataSource
&& src.isFileSupported(name))
return (OsmMapDataSource) src;
} catch (InstantiationException e) {
// try the next one.
} catch (IllegalAccessException e) {
// try the next one.
} catch (NoClassDefFoundError e) {
// try the next one
}
}
// Give up and assume it is in the XML format. If it isn't we will get
// an
// error soon enough anyway.
return new Osm5CoastDataSource();
}
private Collection<Way> loadFile(String filename)
throws FileNotFoundException {
OsmMapDataSource src = loadFromFile(filename);
return src.getElementSaver().getWays().values();
}
private EnhancedProperties getConfig() {
return coastConfig;
}
private synchronized void loadCoastlinesImpl() {
log.info("Load coastlines");
for (String coastlineFile : coastlineFiles) {
try {
int nBefore = coastlines.size();
Collection<Way> loadedCoastlines = loadFile(coastlineFile);
log.info(loadedCoastlines.size(), "coastline ways from",
coastlineFile, "loaded.");
ArrayList<Way> ways = SeaGenerator.joinWays(loadedCoastlines);
ListIterator<Way> wayIter = ways.listIterator();
ways = null;
while (wayIter.hasNext()) {
Way way = wayIter.next();
wayIter.remove();
coastlines.add(new CoastlineWay(way.getId(), way
.getPoints()));
}
log.info((coastlines.size() - nBefore),
"coastlines loaded from", coastlineFile);
} catch (FileNotFoundException exp) {
log.error("Coastline file " + coastlineFile + " not found.");
} catch (Exception exp) {
log.error(exp);
exp.printStackTrace();
}
}
coastlinesLoaded.set(true);
}
public Collection<Way> getCoastlines(Area bbox) {
if (coastlinesLoaded.get() == false) {
synchronized (this) {
loadCoastlines();
}
}
Collection<Way> ways = new ArrayList<Way>();
for (CoastlineWay w : coastlines) {
if (w.getBbox().intersects(bbox)) {
Way x = new Way(w.getOriginalId(), w.getPoints());
x.setFakeId();
x.addTag("natural", "coastline");
ways.add(x);
}
}
return ways;
}
public static class CoastlineWay extends Way {
private final Area bbox;
public CoastlineWay(long id, List<Coord> points) {
super(id, points);
if (points.isEmpty()) {
throw new IllegalArgumentException(
"No support for empty ways. WayId: " + id);
}
if (log.isDebugEnabled())
log.debug("Create coastline way", id, "with", points.size(),
"points");
Coord firstPoint = getPoints().get(0);
int minLat = firstPoint.getLatitude();
int maxLat = firstPoint.getLatitude();
int minLong = firstPoint.getLongitude();
int maxLong = firstPoint.getLongitude();
for (Coord c : getPoints()) {
if (c.getLatitude() < minLat) {
minLat = c.getLatitude();
} else if (c.getLatitude() > maxLat) {
maxLat = c.getLatitude();
}
if (c.getLongitude() < minLong) {
minLong = c.getLongitude();
} else if (c.getLongitude() > maxLong) {
maxLong = c.getLongitude();
}
}
bbox = new Area(minLat, minLong, maxLat, maxLong);
}
@Override
public String getTag(String key) {
if ("natural".equals(key)) {
return "coastline";
} else {
return null;
}
}
@Override
public String toTagString() {
return "[natural=coastline]";
}
@Override
public Map<String, String> getTagsWithPrefix(String prefix,
boolean removePrefix) {
if ("natural".startsWith(prefix)) {
if (removePrefix) {
return Collections.singletonMap(
"natural".substring(prefix.length()), "coastline");
} else {
return Collections.singletonMap("natural", "coastline");
}
} else {
return Collections.emptyMap();
}
}
@Override
protected void removeAllTags() {
}
@Override
public Iterable<Entry<String, String>> getTagEntryIterator() {
return Collections.singletonMap("natural", "coastline").entrySet();
}
public Area getBbox() {
return bbox;
}
}
}