/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.map.writer.osmosis;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.mapsforge.core.model.BoundingBox;
import org.mapsforge.map.writer.HDTileBasedDataProcessor;
import org.mapsforge.map.writer.MapFileWriter;
import org.mapsforge.map.writer.RAMTileBasedDataProcessor;
import org.mapsforge.map.writer.model.MapWriterConfiguration;
import org.mapsforge.map.writer.model.TileBasedDataProcessor;
import org.mapsforge.map.writer.util.Constants;
import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.domain.v0_6.Node;
import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
import org.openstreetmap.osmosis.core.domain.v0_6.Way;
import org.openstreetmap.osmosis.core.task.v0_6.Sink;
/**
* An Osmosis plugin that reads OpenStreetMap data and converts it to a mapsforge binary file.
*
* @author bross
*/
public class MapFileWriterTask implements Sink {
private static final Logger LOGGER = Logger.getLogger(MapFileWriterTask.class.getName());
// Accounting
private int amountOfNodesProcessed = 0;
private int amountOfWaysProcessed = 0;
private int amountOfRelationsProcessed = 0;
private final MapWriterConfiguration configuration;
private TileBasedDataProcessor tileBasedGeoObjectStore;
MapFileWriterTask(MapWriterConfiguration configuration) {
this.configuration = configuration;
Properties properties = new Properties();
try {
properties.load(MapFileWriterTask.class.getClassLoader().getResourceAsStream("default.properties"));
configuration.setWriterVersion(Constants.CREATOR_NAME + "-"
+ properties.getProperty(Constants.PROPERTY_NAME_WRITER_VERSION));
configuration.setFileSpecificationVersion(Integer.parseInt(properties
.getProperty(Constants.PROPERTY_NAME_FILE_SPECIFICATION_VERSION)));
LOGGER.info("mapfile-writer version: " + configuration.getWriterVersion());
LOGGER.info("mapfile format specification version: " + configuration.getFileSpecificationVersion());
} catch (IOException e) {
throw new RuntimeException("could not find default properties", e);
} catch (NumberFormatException e) {
throw new RuntimeException("map file specification version is not an integer", e);
}
// CREATE DATASTORE IF BBOX IS DEFINED
if (this.configuration.getBboxConfiguration() != null) {
if ("ram".equalsIgnoreCase(configuration.getDataProcessorType())) {
this.tileBasedGeoObjectStore = RAMTileBasedDataProcessor.newInstance(configuration);
} else {
this.tileBasedGeoObjectStore = HDTileBasedDataProcessor.newInstance(configuration);
}
}
}
/*
* (non-Javadoc)
* @see org.openstreetmap.osmosis.core.task.v0_6.Initializable#initialize(java.util.Map)
*/
@Override
public void initialize(Map<String, Object> metadata) {
// nothing to do here
}
@Override
public final void complete() {
NumberFormat nfMegabyte = NumberFormat.getInstance();
NumberFormat nfCounts = NumberFormat.getInstance();
nfCounts.setGroupingUsed(true);
nfMegabyte.setMaximumFractionDigits(2);
LOGGER.info("completing read...");
this.tileBasedGeoObjectStore.complete();
LOGGER.info("start writing file...");
try {
if (this.configuration.getOutputFile().exists()) {
LOGGER.info("overwriting file " + this.configuration.getOutputFile().getAbsolutePath());
this.configuration.getOutputFile().delete();
}
MapFileWriter.writeFile(this.configuration, this.tileBasedGeoObjectStore);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "error while writing file", e);
}
LOGGER.info("finished...");
LOGGER.fine("total processed nodes: " + nfCounts.format(this.amountOfNodesProcessed));
LOGGER.fine("total processed ways: " + nfCounts.format(this.amountOfWaysProcessed));
LOGGER.fine("total processed relations: " + nfCounts.format(this.amountOfRelationsProcessed));
LOGGER.info("estimated memory consumption: "
+ nfMegabyte.format(+((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / Math
.pow(1024, 2))) + "MB");
}
@Override
public final void release() {
if (this.tileBasedGeoObjectStore != null) {
this.tileBasedGeoObjectStore.release();
}
}
@Override
public final void process(EntityContainer entityContainer) {
Entity entity = entityContainer.getEntity();
switch (entity.getType()) {
case Bound:
Bound bound = (Bound) entity;
if (this.configuration.getBboxConfiguration() == null) {
BoundingBox bbox = new BoundingBox(bound.getBottom(), bound.getLeft(), bound.getTop(),
bound.getRight());
this.configuration.setBboxConfiguration(bbox);
this.configuration.validate();
if ("ram".equals(this.configuration.getDataProcessorType())) {
this.tileBasedGeoObjectStore = RAMTileBasedDataProcessor.newInstance(this.configuration);
} else {
this.tileBasedGeoObjectStore = HDTileBasedDataProcessor.newInstance(this.configuration);
}
}
LOGGER.info("start reading data...");
break;
// *******************************************************
// ****************** NODE PROCESSING*********************
// *******************************************************
case Node:
if (this.tileBasedGeoObjectStore == null) {
LOGGER.severe("No valid bounding box found in input data.\n"
+ "Please provide valid bounding box via command "
+ "line parameter 'bbox=minLat,minLon,maxLat,maxLon'.\n"
+ "Tile based data store not initialized. Aborting...");
throw new IllegalStateException("tile based data store not initialized, missing bounding "
+ "box information in input data");
}
this.tileBasedGeoObjectStore.addNode((Node) entity);
// hint to GC
entity = null;
this.amountOfNodesProcessed++;
break;
// *******************************************************
// ******************* WAY PROCESSING*********************
// *******************************************************
case Way:
this.tileBasedGeoObjectStore.addWay((Way) entity);
entity = null;
this.amountOfWaysProcessed++;
break;
// *******************************************************
// ****************** RELATION PROCESSING*********************
// *******************************************************
case Relation:
Relation currentRelation = (Relation) entity;
this.tileBasedGeoObjectStore.addRelation(currentRelation);
this.amountOfRelationsProcessed++;
entity = null;
break;
}
}
}