/** * ***************************************************************************** * Copyright 2013 Johannes Mitlmeier * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * **************************************************************************** */ package de.fub.agg2graph.osm; import de.fub.agg2graph.graph.RamerDouglasPeuckerFilter; import de.fub.agg2graph.roadgen.Road; import de.fub.agg2graph.roadgen.Road.RoadType; import de.fub.agg2graph.roadgen.RoadNetwork; import de.fub.agg2graph.structs.ClassObjectEditor; import de.fub.agg2graph.structs.ILocation; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; /** * Export a {@link RoadNetwork} to an xml file as accepted by OpenStreetMap. * * @author Johannes Mitlmeier * */ public class OsmExporter implements IExporter { private static final Logger LOG = Logger.getLogger(OsmExporter.class.getName()); public String user = "agg2graph"; public String uid = "1"; public int osmNodeStartId = 1; public int osmWayStartId = 1; private String dateString; private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); /* * XML templates, see * http://wiki.openstreetmap.org/wiki/OSM_XML#OSM_XML_file_format */ // TODO calculate bounds (or take them from somewhere) private static final String XML_FILE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<osm version=\"0.6\" generator=\"agg2graph\">\n" + "<bounds minlat=\"%s\" minlon=\"%s\" maxlat=\"%s\" maxlon=\"%s\"/>\n%s</osm>"; /* http://wiki.openstreetmap.org/wiki/Node */ private static final String XML_NODE_PATTERN = "<node id=\"%d\" lat=\"%.7f\" lon=\"%.7f\" version=\"1\" changeset=\"1\" user=\"%s\" uid=\"%s\" visible=\"true\" timestamp=\"%s\"></node>\n"; // timestamp format: 2007-01-28T11:40:26Z /* http://wiki.openstreetmap.org/wiki/Way */ private static final String XML_WAY_PATTERN = "<way id=\"%s\" highway=\"%s\" %s visible=\"true\" timestamp=\"%s\" version=\"1\" changeset=\"1\" user=\"%s\" uid=\"%s\">\n%s</way>\n"; private static final String XML_WAY_NODE_REF = "<nd ref=\"%s\"/>\n"; public final RamerDouglasPeuckerFilter RDPF = new RamerDouglasPeuckerFilter(10); @Override public void export(RoadNetwork roadNetwork, OutputStream outputStream) { String content = getXmlData(roadNetwork); // write to file try { BufferedWriter out = new BufferedWriter(new OutputStreamWriter(outputStream)); out.write(content); out.close(); } catch (IOException e) { LOG.log(Level.SEVERE, e.getMessage(), e); } } /** * Get a string representation of the data for exporting to a xml file. * * @param roadNetwork * @return */ private String getXmlData(RoadNetwork roadNetwork) { StringBuilder sb = new StringBuilder(); int currentNodeID = osmNodeStartId; int currentWayID = osmWayStartId; dateString = dateFormatter.format(new Date()); ArrayList<Way> ways = new ArrayList<>(); // generate output for (Road road : roadNetwork.getRoads()) { if (road.isVisible()) { // write nodes as OSM nodes List<? extends ILocation> simplifiedPath = road.getNodes(); //RDPF.simplify(road.getPath()); if (!simplifiedPath.isEmpty()) { Way way = new Way(String.valueOf(currentWayID++)); way.setRoadType(road.getType()); way.setOneWay(road.isOneWay()); ways.add(way); for (ILocation node : simplifiedPath) { sb.append(getNodeString(node, currentNodeID)); way.getNds().add(new Nd(String.valueOf(currentNodeID++))); } } } } for (Way way : ways) { sb.append(getWayString(way)); } String xmlContent = String.format(XML_FILE, 0, 0, 0, 0, sb.toString()); return xmlContent; } private String getWayString(Way way) { StringBuilder sb = new StringBuilder(); ILocation node; for (Nd nd : way.getNds()) { sb.append(String.format(XML_WAY_NODE_REF, nd.getRef())); } String type = "unknown"; switch (way.getRoadType()) { case PRIMARY: type = "primary"; break; case SECONDARY: type = "secondary"; break; case TERTIARY: type = "tertiary"; break; } return String.format(Locale.ENGLISH, XML_WAY_PATTERN, way.getId(), type, way.isOneWay() ? "oneway=\"yes\"" : "", dateString, user, uid, sb.toString()); } private String getNodeString(ILocation loc, int currentNodeID) { return String.format(Locale.ENGLISH, XML_NODE_PATTERN, currentNodeID, loc.getLat(), loc.getLon(), user, uid, dateString); } @Override public List<ClassObjectEditor> getSettings() { ArrayList<ClassObjectEditor> result = new ArrayList<ClassObjectEditor>(3); result.add(new ClassObjectEditor(this, Arrays.asList(new String[]{"rdpf", "targetFile"}))); result.add(new ClassObjectEditor(this.RDPF)); return result; } private static class Nd { private String ref = null; public Nd(String nodeRef) { this.ref = nodeRef; } public String getRef() { return ref; } public void setRef(String ref) { this.ref = ref; } } private static class Way { private ArrayList<Nd> nds = new ArrayList<>(); private String id; private Road.RoadType roadType = RoadType.UNKNOWN; private boolean oneWay = true; public Way(String id) { this.id = id; } public ArrayList<Nd> getNds() { return nds; } public boolean isOneWay() { return oneWay; } public void setOneWay(boolean oneWay) { this.oneWay = oneWay; } public String getId() { return id; } public void setId(String id) { this.id = id; } public RoadType getRoadType() { return roadType; } public void setRoadType(RoadType roadType) { this.roadType = roadType; } } }