package com.bitmonlab.osiris.imports.map.managers.impl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.eclipse.jetty.util.log.Log; import org.openstreetmap.osmosis.core.Osmosis; import org.opentripplanner.graph_builder.GraphBuilderTask; import org.opentripplanner.graph_builder.impl.CheckGeometryGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.TransitToStreetNetworkGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.osm.DefaultWayPropertySetSource; import org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.osm.PortlandCustomNamer; import org.opentripplanner.graph_builder.impl.transit_index.TransitIndexBuilder; import org.opentripplanner.graph_builder.services.GraphBuilder; import org.opentripplanner.graph_builder.services.GraphBuilderWithGtfsDao; import org.opentripplanner.openstreetmap.impl.AnyFileBasedOpenStreetMapProviderImpl; import org.springframework.beans.factory.annotation.Value; import ch.qos.logback.classic.Level; import com.bitmonlab.osiris.commons.map.model.geojson.Feature; import com.bitmonlab.osiris.commons.map.model.geojson.LineString; import com.bitmonlab.osiris.commons.map.model.geojson.MetaData; import com.bitmonlab.osiris.commons.map.model.geojson.Point; import com.bitmonlab.osiris.commons.map.model.geojson.Polygon; import com.bitmonlab.osiris.imports.map.dao.api.ImportFilesRepository; import com.bitmonlab.osiris.imports.map.dao.api.ImportRepository; import com.bitmonlab.osiris.imports.map.dao.api.MetaDataImportRepository; import com.bitmonlab.osiris.imports.map.exceptions.BackgroundMapBuilderException; import com.bitmonlab.osiris.imports.map.exceptions.ExecutionNotAllowed; import com.bitmonlab.osiris.imports.map.exceptions.GraphBuilderException; import com.bitmonlab.osiris.imports.map.exceptions.ImportFilesException; import com.bitmonlab.osiris.imports.map.exceptions.InternalErrorException; import com.bitmonlab.osiris.imports.map.exceptions.ParseMapException; import com.bitmonlab.osiris.imports.map.exceptions.QueryException; import com.bitmonlab.osiris.imports.map.exceptions.RoutingFileNotExistsException; import com.bitmonlab.osiris.imports.map.managers.api.ImportOSMFileManager; import com.bitmonlab.osiris.imports.map.model.osm.Bounds; import com.bitmonlab.osiris.imports.map.model.osm.Member; import com.bitmonlab.osiris.imports.map.model.osm.ND; import com.bitmonlab.osiris.imports.map.model.osm.Node; import com.bitmonlab.osiris.imports.map.model.osm.OSM; import com.bitmonlab.osiris.imports.map.model.osm.Relation; import com.bitmonlab.osiris.imports.map.model.osm.Tag; import com.bitmonlab.osiris.imports.map.model.osm.Way; import com.bitmonlab.osiris.imports.map.utils.Cryptography; @Named public class ImportOSMFileManagerImpl implements ImportOSMFileManager { @Inject private ImportRepository importRepository; @Inject private MetaDataImportRepository metaDataRepository; @Inject private ImportFilesRepository importFilesRepository; @Value("${pathRootUser}") private String pathRootUser; private final String file_osm = "map.osm"; private final String file_map = "background.map"; private final String file_obj = "Graph.obj"; private static Logger logger = Logger.getLogger(ImportOSMFileManagerImpl.class); public Collection<Feature> importOSMFile(final String appIdentifier, final InputStream data, boolean bGraphBuilder) throws ExecutionNotAllowed, InternalErrorException, ParseMapException, QueryException, IOException, NoSuchAlgorithmException, BackgroundMapBuilderException, ImportFilesException, RoutingFileNotExistsException, GraphBuilderException{ OSM osm = null; OutputStream os = null; String jsonStr = null; String objStr = null; final String pathUser = pathRootUser.concat(File.separator) .concat(appIdentifier).concat(File.separator); final String strOSMFile = pathUser.concat(file_osm); File dPathUser = new File(pathUser); if (!dPathUser.exists()) { boolean createdPathUser=dPathUser.mkdirs(); if(!createdPathUser){ logger.error("Cannot create user path"); throw new InternalErrorException(); } } if (importRepository.isImportProcessLocked(appIdentifier)) { throw new ExecutionNotAllowed(); } else { importRepository.lockImportProcess(appIdentifier); } try { //Parse map .osm file (xml format) JAXBContext context = JAXBContext.newInstance(OSM.class); Unmarshaller unmarshaller = context.createUnmarshaller(); if(bGraphBuilder){ unmarshaller.setListener(new UnMarshallerListenerGraph()); }else{ unmarshaller.setListener(new UnMarshallerListener()); } osm = (OSM) unmarshaller.unmarshal(data); Marshaller marshaller = context.createMarshaller(); MarshallerListener mlistener = null; if (osm.getBounds() == null) { mlistener = new MarshallerListener(); marshaller.setListener(mlistener); } os = new FileOutputStream(strOSMFile); marshaller.marshal(osm, os); os.close(); //Convert OSM to GeoJson and save in MongoDB Collection<Feature> featureCollection = transformOSMFileToGeoJson( osm, appIdentifier); jsonStr=importRepository.saveGeoJson(appIdentifier,featureCollection); //Calculate Bounds of Map Bounds boundsMap = new Bounds(); if (mlistener != null) { boundsMap = mlistener.getBoundsMap(); } else { boundsMap = osm.getBounds(); } //Build Graph for routing, if(bGraphBuilder){ graphBuilder(appIdentifier, pathUser, strOSMFile); objStr=readObjFile(appIdentifier); } //background for app and create Metadata of Map MetaData metaData = generateMetaData(jsonStr, objStr, boundsMap, appIdentifier, bGraphBuilder); metaDataRepository.save(metaData); backgroundMapBuilder(appIdentifier, pathUser, strOSMFile, boundsMap); saveImportFiles(appIdentifier, pathUser, bGraphBuilder); deleteImportFilesDisk(pathUser); return featureCollection; } catch (JAXBException parseException) { parseException.printStackTrace(); throw new ParseMapException(); } catch (IOException ioe) { logger.error("I/O Excepction"); ioe.printStackTrace(); throw new InternalErrorException(); } finally { importRepository.deleteLockImportProcess(appIdentifier); if (os != null) { os.close(); } } } private String readObjFile(String appIdentifier) throws RoutingFileNotExistsException{ String objPath=pathRootUser.concat(File.separator).concat(appIdentifier).concat(File.separator).concat(file_obj); File objFile=new File(objPath); String objFileText; try { objFileText = FileUtils.readFileToString(objFile); } catch (IOException e) { throw new RoutingFileNotExistsException(); } return objFileText; } private void graphBuilder(final String appIdentifier, final String pathUser, final String strOSMFile) throws GraphBuilderException{ try { GraphBuilderTask graphBuilderTask = new GraphBuilderTask(); GtfsGraphBuilderImpl gtfsBuilder = new GtfsGraphBuilderImpl(); List<GraphBuilderWithGtfsDao> gtfsGraphBuilders = new ArrayList<GraphBuilderWithGtfsDao>(); TransitIndexBuilder transitIndexBuilder = new TransitIndexBuilder(); gtfsGraphBuilders.add(transitIndexBuilder); gtfsBuilder.setGtfsGraphBuilders(gtfsGraphBuilders); AnyFileBasedOpenStreetMapProviderImpl anyFileBasedOpenStreetMapProviderImpl = new AnyFileBasedOpenStreetMapProviderImpl(); File fileOSM = new File(strOSMFile); anyFileBasedOpenStreetMapProviderImpl.setPath(fileOSM); OpenStreetMapGraphBuilderImpl osmBuilder = new OpenStreetMapGraphBuilderImpl(); osmBuilder.setProvider(anyFileBasedOpenStreetMapProviderImpl); osmBuilder .setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); osmBuilder.setCustomNamer(new PortlandCustomNamer()); List<GraphBuilder> graphLoaders = new ArrayList<GraphBuilder>(); graphLoaders.add(osmBuilder); graphLoaders.add(new CheckGeometryGraphBuilderImpl()); graphLoaders.add(new TransitToStreetNetworkGraphBuilderImpl()); graphBuilderTask.setGraphBuilders(graphLoaders); graphBuilderTask.setPath(pathUser); graphBuilderTask.run(); } catch (Exception e) { throw new GraphBuilderException(); } } public Collection<Feature> transformOSMFileToGeoJson(final OSM osm, final String appIdentifier){ List<Feature> featureCollection = new ArrayList<Feature>(); osm.sortNodes(); osm.sortWays(); osm.sortRelations(); if (osm.getRelations() != null) { for (Relation relation : osm.getRelations()) { relationToPolygon(relation, osm, null, featureCollection, appIdentifier); } } if (osm.getWays() != null) { for (Way way : osm.getWays()) { wayToLineString(way, osm, null, featureCollection, appIdentifier); } } if (osm.getNodes() != null) { for (Node node : osm.getNodes()) { nodeToPoint(appIdentifier, node, null, featureCollection); } } return featureCollection; } private Feature relationToPolygon(final Relation relation, final OSM osm, final List<Map<String, String>> inheritedProperties, final List<Feature> featureCollection, final String appIdentifier){ Feature feature = new Feature(); feature.setId(relation.getId()); int pos = searchFeature(featureCollection, feature); if (pos < 0) { if (inheritedProperties != null) { feature.setPropertiesRelations(inheritedProperties); } Collection<List<List<Double>>> coordinatesPolygon = new ArrayList<List<List<Double>>>(); Map<String, String> properties = tagsToProperties( relation.getTags(), relation.getId(), "relation"); feature.setProperties(properties); if(relation.getMembers()!=null){ for (Member member : relation.getMembers()) { if (!member.getRef().equals(relation.getId())) { Feature featureChild = createRelationsChilds(osm, feature, member, featureCollection, appIdentifier); if (!member.getType().equals("relation") && featureChild != null && featureChild.getGeometry() != null && (featureChild.getGeometry() instanceof LineString) && (member.getRole().equals("shell") || (properties .get("type").equals("multipolygon") && (member .getRole().equals("inner") || member .getRole().equals("outer"))))) { LineString lineString = (LineString) featureChild .getGeometry(); List<List<Double>> coordinatesList = new ArrayList<List<Double>>(); coordinatesList.addAll(lineString.getCoordinates()); Point firstPoint = new Point(); firstPoint .setCoordinates((List<Double>) coordinatesList .get(0)); Point lastPoint = new Point(); lastPoint.setCoordinates((List<Double>) coordinatesList .get(coordinatesList.size() - 1)); if (!firstPoint.equals(lastPoint)) { coordinatesList.add(coordinatesList.get(0)); } coordinatesPolygon .add((ArrayList<List<Double>>) coordinatesList); } // end if 2 } // end if 1 }// end for } Polygon poly = null; if (!coordinatesPolygon.isEmpty()) { poly = new Polygon(); poly.setCoordinates(coordinatesPolygon); } feature.setGeometry(poly); featureCollection.add(feature); sortFeatures(featureCollection); } else { if (inheritedProperties != null) { feature = featureCollection.get(pos); feature.updateProperties(inheritedProperties); for (Member m : relation.getMembers()) { createRelationsChilds(osm, feature, m, featureCollection, appIdentifier); } } } return feature; } private Feature wayToLineString(final Way way, final OSM osm, final List<Map<String, String>> inheritedProperties, final List<Feature> featureCollection, final String appIdentifier){ Feature feature = new Feature(); feature.setId(way.getId()); int pos = searchFeature(featureCollection, feature); if (pos < 0) { if (inheritedProperties != null) { feature.setPropertiesRelations(inheritedProperties); } Map<String, String> properties = tagsToProperties(way.getTags(), way.getId(), "way"); feature.setProperties(properties); LineString ls = new LineString(); List<List<Double>> coordinatesList = new ArrayList<List<Double>>(); List<Map<String, String>> parentProperties = feature .getAllProperties(); if (way.getNds() != null && osm.getNodes() != null) { for (ND n : way.getNds()) { int posNode = osm.searchNode(new Node(n.getRef())); if (posNode >= 0) { Node node = osm.getNodes().get(posNode); Point nodePoint = new Point(); nodePoint.setCoordinates(node.getNodeCoordinates()); boolean bInsert = true; ListIterator<List<Double>> it = coordinatesList .listIterator(); while (bInsert && it.hasNext()) { List<Double> coordinate = it.next(); Point otherPoint = new Point(); otherPoint.setCoordinates(coordinate); if (nodePoint.equals(otherPoint) && !(it.nextIndex() - 1 == 0 && way .getNds().indexOf(n) == way .getNds().size() - 1)) { bInsert = false; } } if (bInsert) { coordinatesList.add((ArrayList<Double>) node .getNodeCoordinates()); } if(way.existTag("@graphIndoor")){ if(n.getRef().equals(way.getNds().get(0).getRef()) || n.getRef().equals(way.getNds().get(way.getNds().size()-1).getRef())){ Tag extremTag = new Tag(); extremTag.setK("@end"); extremTag.setV("true"); if(node.getTags()!=null){ node.getTags().add(extremTag); } else{ List<Tag> tags=new ArrayList<Tag>(); tags.add(extremTag); node.setTags(tags); } } } nodeToPoint(appIdentifier, node, parentProperties, featureCollection); } } } if (coordinatesList.isEmpty()) { ls = null; } else { ls.setCoordinates(coordinatesList); ls.calculateCentroid(); } feature.setGeometry(ls); featureCollection.add(feature); sortFeatures(featureCollection); } else { if (inheritedProperties != null) { feature = featureCollection.get(pos); feature.updateProperties(inheritedProperties); for (ND n : way.getNds()) { int positionNode = osm.searchNode(new Node(n.getRef())); if (positionNode >= 0) { Node node = osm.getNodes().get(positionNode); nodeToPoint(appIdentifier, node, feature.getAllProperties(), featureCollection); } } } } return feature; } private Feature nodeToPoint(final String appIdentifier, final Node node, final List<Map<String, String>> inheritedProperties, final List<Feature> featureCollection){ Feature feature = null; feature = new Feature(); feature.setId(node.getId()); int pos = searchFeature(featureCollection, feature); if (pos < 0) { if (inheritedProperties != null) { feature.setPropertiesRelations(inheritedProperties); } Map<String, String> properties = tagsToProperties(node.getTags(), node.getId(), "node"); feature.setProperties(properties); Point point = new Point(); point.setCoordinates(node.getNodeCoordinates()); feature.setGeometry(point); featureCollection.add(feature); sortFeatures(featureCollection); } else { if (inheritedProperties != null) { feature = featureCollection.get(pos); feature.updateProperties(inheritedProperties); } } return feature; } private int searchFeature(final List<Feature> featureCollection, final Feature feature) { int pos = -1; if (featureCollection != null && feature != null) { pos = Collections.binarySearch(featureCollection, feature); } return pos; } private Map<String, String> tagsToProperties(final List<Tag> tagslist, final String id, final String type) { Map<String, String> properties = new HashMap<String, String>(); properties.put("@id", id); properties.put("@type", type); if (tagslist != null) { for (Tag t : tagslist) { properties.put(t.getK(), t.getV()); } } return properties; } private Feature createRelationsChilds( final OSM osm, final Feature featureParent, final Member member, final List<Feature> featureCollection, final String appIdentifier){ Feature featureChild = null; List<Map<String, String>> parentProperties = featureParent .getAllProperties(); if (member.getType().equals("node") && osm.getNodes() != null) { int pos = osm.searchNode(new Node(member.getRef())); if (pos >= 0) { Node node = osm.getNodes().get(pos); featureChild = nodeToPoint(appIdentifier, node, parentProperties, featureCollection); } } else if (member.getType().equals("way") && osm.getWays() != null) { int pos = osm.searchWay(new Way(member.getRef())); if (pos >= 0) { Way way = osm.getWays().get(pos); featureChild = wayToLineString(way, osm, parentProperties, featureCollection, appIdentifier); } } else if (member.getType().equals("relation") && osm.getRelations() != null) { int pos = osm.searchRelation(new Relation(member.getRef())); if (pos >= 0) { Relation relationChild = osm.getRelations().get(pos); relationToPolygon(relationChild, osm, parentProperties, featureCollection, appIdentifier); } } if (featureChild != null && featureChild.getProperties() != null && member.getRole() != null) { featureChild.getProperties().put("@role", member.getRole()); } return featureChild; } private void sortFeatures(final List<Feature> featuresList) { if (featuresList != null) { Collections.sort((List<Feature>) featuresList); } } private MetaData generateMetaData(String strjson, String objStr,Bounds boundsMap, String appId, boolean bGraphBuilder) throws NoSuchAlgorithmException{ String strChkSum = Cryptography.calculateCheckSum(strjson); MetaData metaData = new MetaData(); metaData.setAppId(appId); metaData.setOSMChecksum(strChkSum); if(bGraphBuilder){ String strChkSumObj = Cryptography.calculateCheckSum(objStr); metaData.setRoutingChecksum(strChkSumObj); } metaData.setMaxlat(Double.valueOf(boundsMap.getMaxlat())); metaData.setMaxlon(Double.valueOf(boundsMap.getMaxlon())); metaData.setMinlat(Double.valueOf(boundsMap.getMinlat())); metaData.setMinlon(Double.valueOf(boundsMap.getMinlon())); return metaData; } private void backgroundMapBuilder(final String appIdentifier, final String pathUser, final String osmFile, Bounds boundsMap) throws BackgroundMapBuilderException{ try { String[] args = { "-plugin", "org.mapsforge.map.writer.osmosis.MapFileWriterPluginLoader", "--read-xml", osmFile, "--mapfile-writer", "file=".concat(pathUser.concat(file_map)), "bbox=".concat(boundsMap.getMinlat()).concat(",") .concat(boundsMap.getMinlon()).concat(",") .concat(boundsMap.getMaxlat()).concat(",") .concat(boundsMap.getMaxlon())}; Osmosis.run(args); } catch (Exception e) { e.printStackTrace(); throw new BackgroundMapBuilderException(); } } private void saveImportFiles(String appIdentifier, String pathUser, boolean bGraphBuilder) throws IOException, ImportFilesException{ String pathOSM = pathUser.concat(file_osm); String pathMap = pathUser.concat(file_map); File fileOSM = new File(pathOSM); File fileMap = new File(pathMap); if (fileOSM.exists() && fileMap.exists()) { importFilesRepository.saveFileOSM(appIdentifier, fileOSM); importFilesRepository.saveFileMap(appIdentifier, fileMap); if(bGraphBuilder){ String pathObj = pathUser.concat(file_obj); File fileObj = new File(pathObj); importFilesRepository.saveFileObj(appIdentifier, fileObj); } } else { throw new ImportFilesException(); } } private void deleteImportFilesDisk(String pathUser) throws ImportFilesException{ File folder=new File(pathUser); if(folder.exists()&&folder.isDirectory()){ File[] files = folder.listFiles(); for(File file: files){ file.delete(); } } else{ throw new ImportFilesException(); } } }