Ticket #6886: patchV2.diff

File patchV2.diff, 37.6 KB (added by Don-vip, 15 years ago)
  • src/org/openstreetmap/josm/actions/ExtensionFileFilter.java

     
    4040        String[] importerNames = {
    4141                "org.openstreetmap.josm.io.OsmImporter",
    4242                "org.openstreetmap.josm.io.OsmGzipImporter",
     43                "org.openstreetmap.josm.io.PbfImporter",
    4344                "org.openstreetmap.josm.io.GpxImporter",
    4445                "org.openstreetmap.josm.io.NMEAImporter",
    4546                "org.openstreetmap.josm.io.OsmBzip2Importer",
  • src/org/openstreetmap/josm/data/coor/LatLon.java

     
    6363    public static boolean isValidLon(double lon) {
    6464        return lon >= -180d && lon <= 180d;
    6565    }
     66   
     67    /**
     68     * Replies true if lat is in the range [-90,90] and lon is in the range [-180,180]
     69     *
     70     * @return true if lat is in the range [-90,90] and lon is in the range [-180,180]
     71     */
     72    public boolean isValid() {
     73        return isValidLat(lat()) && isValidLon(lon());
     74    }
    6675
    6776    /**
    6877     * Replies the coordinate in degrees/minutes/seconds format
  • src/org/openstreetmap/josm/io/AbstractReader.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.util.ArrayList;
     7import java.util.Collection;
     8import java.util.HashMap;
     9import java.util.List;
     10import java.util.Map;
     11
     12import org.openstreetmap.josm.data.osm.Changeset;
     13import org.openstreetmap.josm.data.osm.DataSet;
     14import org.openstreetmap.josm.data.osm.Node;
     15import org.openstreetmap.josm.data.osm.OsmPrimitive;
     16import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     17import org.openstreetmap.josm.data.osm.PrimitiveId;
     18import org.openstreetmap.josm.data.osm.Relation;
     19import org.openstreetmap.josm.data.osm.RelationMember;
     20import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     21import org.openstreetmap.josm.data.osm.Way;
     22
     23/**
     24 * Base class for OsmReader (XML file format) and PbfReader (Protobuf file format)
     25 * @author Vincent
     26 *
     27 */
     28public abstract class AbstractReader {
     29   
     30    /**
     31     * Used as a temporary storage for relation members, before they
     32     * are resolved into pointers to real objects.
     33     */
     34    protected static class RelationMemberData {
     35        public OsmPrimitiveType type;
     36        public long id;
     37        public String role;
     38    }
     39
     40    /**
     41     * The dataset to add parsed objects to.
     42     */
     43    protected final DataSet ds = new DataSet();
     44
     45    protected Changeset uploadChangeset;
     46
     47    /** the map from external ids to read OsmPrimitives. External ids are
     48     * longs too, but in contrast to internal ids negative values are used
     49     * to identify primitives unknown to the OSM server
     50     */
     51    protected Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
     52
     53    /**
     54     * Data structure for the remaining way objects
     55     */
     56    protected Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
     57
     58    /**
     59     * Data structure for relation objects
     60     */
     61    protected Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
     62
     63    /**
     64     * Replies the parsed data set
     65     *
     66     * @return the parsed data set
     67     */
     68    public DataSet getDataSet() {
     69        return ds;
     70    }
     71   
     72    /**
     73     * Processes the parsed nodes after parsing. Just adds them to
     74     * the dataset
     75     *
     76     */
     77    private void processNodesAfterParsing() {
     78        for (OsmPrimitive primitive: externalIdMap.values()) {
     79            if (primitive instanceof Node) {
     80                this.ds.addPrimitive(primitive);
     81            }
     82        }
     83    }
     84
     85    /**
     86     * Processes the ways after parsing. Rebuilds the list of nodes of each way and
     87     * adds the way to the dataset
     88     *
     89     * @throws IllegalDataException thrown if a data integrity problem is detected
     90     */
     91    private void processWaysAfterParsing() throws IllegalDataException{
     92        for (Long externalWayId: ways.keySet()) {
     93            Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
     94            List<Node> wayNodes = new ArrayList<Node>();
     95            for (long id : ways.get(externalWayId)) {
     96                Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
     97                if (n == null) {
     98                    if (id <= 0)
     99                        throw new IllegalDataException (
     100                                tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
     101                                        externalWayId,
     102                                        id));
     103                    // create an incomplete node if necessary
     104                    //
     105                    n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE);
     106                    if (n == null) {
     107                        n = new Node(id);
     108                        ds.addPrimitive(n);
     109                    }
     110                }
     111                if (n.isDeleted()) {
     112                    System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId()));
     113                } else {
     114                    wayNodes.add(n);
     115                }
     116            }
     117            w.setNodes(wayNodes);
     118            if (w.hasIncompleteNodes()) {
     119                  System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.",
     120                          externalWayId, w.getNodesCount()));
     121            }
     122            ds.addPrimitive(w);
     123        }
     124    }
     125
     126    /**
     127     * Completes the parsed relations with its members.
     128     *
     129     * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
     130     * relation member refers to a local primitive which wasn't available in the data
     131     *
     132     */
     133    private void processRelationsAfterParsing() throws IllegalDataException {
     134
     135        // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
     136        for (Long externalRelationId : relations.keySet()) {
     137            Relation relation = (Relation) externalIdMap.get(
     138                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
     139            );
     140            ds.addPrimitive(relation);
     141        }
     142
     143        for (Long externalRelationId : relations.keySet()) {
     144            Relation relation = (Relation) externalIdMap.get(
     145                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
     146            );
     147            List<RelationMember> relationMembers = new ArrayList<RelationMember>();
     148            for (RelationMemberData rm : relations.get(externalRelationId)) {
     149                OsmPrimitive primitive = null;
     150
     151                // lookup the member from the map of already created primitives
     152                primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type));
     153
     154                if (primitive == null) {
     155                    if (rm.id <= 0)
     156                        // relation member refers to a primitive with a negative id which was not
     157                        // found in the data. This is always a data integrity problem and we abort
     158                        // with an exception
     159                        //
     160                        throw new IllegalDataException(
     161                                tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
     162                                        externalRelationId,
     163                                        rm.id));
     164
     165                    // member refers to OSM primitive which was not present in the parsed data
     166                    // -> create a new incomplete primitive and add it to the dataset
     167                    //
     168                    primitive = ds.getPrimitiveById(rm.id, rm.type);
     169                    if (primitive == null) {
     170                        switch (rm.type) {
     171                        case NODE:
     172                            primitive = new Node(rm.id); break;
     173                        case WAY:
     174                            primitive = new Way(rm.id); break;
     175                        case RELATION:
     176                            primitive = new Relation(rm.id); break;
     177                        default: throw new AssertionError(); // can't happen
     178                        }
     179
     180                        ds.addPrimitive(primitive);
     181                        externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive);
     182                    }
     183                }
     184                if (primitive.isDeleted()) {
     185                    System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId()));
     186                } else {
     187                    relationMembers.add(new RelationMember(rm.role, primitive));
     188                }
     189            }
     190            relation.setMembers(relationMembers);
     191        }
     192    }
     193
     194    private void processChangesetAfterParsing() {
     195        if (uploadChangeset != null) {
     196            for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) {
     197                ds.addChangeSetTag(e.getKey(), e.getValue());
     198            }
     199        }
     200    }
     201   
     202    protected final void prepareDataSet() throws IllegalDataException {
     203        try {
     204            ds.beginUpdate();
     205            processNodesAfterParsing();
     206            processWaysAfterParsing();
     207            processRelationsAfterParsing();
     208            processChangesetAfterParsing();
     209        } finally {
     210            ds.endUpdate();
     211        }
     212    }
     213}
  • src/org/openstreetmap/josm/io/AllFormatsImporter.java

     
    1212 */
    1313public class AllFormatsImporter extends FileImporter {
    1414    public AllFormatsImporter() {
    15         super(new ExtensionFileFilter("osm,xml,osm.gz,osm.bz2,osm.bz,gpx,gpx.gz,nmea,nme,nma,log,txt,wms,jpg", "", tr("All Formats")
     15        super(new ExtensionFileFilter("osm,xml,osm.gz,osm.bz2,osm.bz,osm.pbf,gpx,gpx.gz,nmea,nme,nma,log,txt,wms,jpg", "", tr("All Formats")
    1616                    + " (*.gpx *.osm *.nmea *.jpg ...)"));
    1717    }
    1818    @Override public boolean acceptFile(File pathname) {
  • src/org/openstreetmap/josm/io/OsmImporter.java

     
    4242    protected void importData(InputStream in, final File associatedFile) throws IllegalDataException {
    4343        final DataSet dataSet = OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
    4444        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
     45        addDataLayer(dataSet, layer, associatedFile.getPath());
     46    }
     47   
     48    protected void addDataLayer(final DataSet dataSet, final OsmDataLayer layer, final String filePath) {
    4549        // FIXME: remove UI stuff from IO subsystem
    4650        //
    4751        Runnable uiStuff = new Runnable() {
     
    5054                if (dataSet.allPrimitives().isEmpty()) {
    5155                    JOptionPane.showMessageDialog(
    5256                            Main.parent,
    53                             tr("No data found in file {0}.", associatedFile.getPath()),
     57                            tr("No data found in file {0}.", filePath),
    5458                            tr("Open OSM file"),
    5559                            JOptionPane.INFORMATION_MESSAGE);
    5660                }
  • src/org/openstreetmap/josm/io/OsmReader.java

     
    88import java.text.MessageFormat;
    99import java.util.ArrayList;
    1010import java.util.Collection;
    11 import java.util.HashMap;
    12 import java.util.LinkedList;
    13 import java.util.List;
    14 import java.util.Map;
    1511import java.util.regex.Matcher;
    1612import java.util.regex.Pattern;
    1713
     14import javax.xml.stream.Location;
    1815import javax.xml.stream.XMLInputFactory;
    19 import javax.xml.stream.XMLStreamReader;
    2016import javax.xml.stream.XMLStreamConstants;
    2117import javax.xml.stream.XMLStreamException;
    22 import javax.xml.stream.Location;
     18import javax.xml.stream.XMLStreamReader;
    2319
    2420import org.openstreetmap.josm.data.Bounds;
    2521import org.openstreetmap.josm.data.coor.LatLon;
     
    2824import org.openstreetmap.josm.data.osm.DataSource;
    2925import org.openstreetmap.josm.data.osm.Node;
    3026import org.openstreetmap.josm.data.osm.NodeData;
    31 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3227import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    3328import org.openstreetmap.josm.data.osm.PrimitiveData;
    34 import org.openstreetmap.josm.data.osm.PrimitiveId;
    3529import org.openstreetmap.josm.data.osm.Relation;
    3630import org.openstreetmap.josm.data.osm.RelationData;
    37 import org.openstreetmap.josm.data.osm.RelationMember;
    38 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    3931import org.openstreetmap.josm.data.osm.Tagged;
    4032import org.openstreetmap.josm.data.osm.User;
    4133import org.openstreetmap.josm.data.osm.Way;
     
    5244 * The XMLStreamReader cursor points to the start of the element, when the method is
    5345 * entered, and it must point to the end of the same element, when it is exited.
    5446 */
    55 public class OsmReader {
    56 
    57     /**
    58      * Used as a temporary storage for relation members, before they
    59      * are resolved into pointers to real objects.
    60      */
    61     private static class RelationMemberData {
    62         public OsmPrimitiveType type;
    63         public long id;
    64         public String role;
    65     }
    66 
    67     /**
    68      * The dataset to add parsed objects to.
    69      */
    70     private DataSet ds = new DataSet();
     47public class OsmReader extends AbstractReader {
    7148
    7249    private XMLStreamReader parser;
    7350
    74     /** the map from external ids to read OsmPrimitives. External ids are
    75      * longs too, but in contrast to internal ids negative values are used
    76      * to identify primitives unknown to the OSM server
    77      */
    78     private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
    79 
    80     /**
    81      * Data structure for the remaining way objects
    82      */
    83     private Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
    84 
    85     /**
    86      * Data structure for relation objects
    87      */
    88     private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
    89 
    90     private Changeset uploadChangeset;
    91 
    92     /**
    93      * constructor (for private use only)
    94      *
    95      * @see #parseDataSet(InputStream, DataSet, ProgressMonitor)
    96      */
    97     private OsmReader() {
    98         externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
    99     }
    100 
    10151    public void setParser(XMLStreamReader parser) {
    10252        this.parser = parser;
    10353    }
    10454
    105     /**
    106      * Replies the parsed data set
    107      *
    108      * @return the parsed data set
    109      */
    110     public DataSet getDataSet() {
    111         return ds;
    112     }
    113 
    11455    protected void throwException(String msg) throws XMLStreamException {
    11556        throw new OsmParsingException(msg, parser.getLocation());
    11657    }
     
    13677        parser.close();
    13778    }
    13879
     80    @SuppressWarnings("null")
    13981    private void parseOsm() throws XMLStreamException {
    14082        String v = parser.getAttributeValue(null, "version");
    14183        if (v == null) {
     
    351293        }
    352294    }
    353295
     296    @SuppressWarnings("null")
    354297    private void parseTag(Tagged t) throws XMLStreamException {
    355298        String key = parser.getAttributeValue(null, "k");
    356299        String value = parser.getAttributeValue(null, "v");
     
    579522    }
    580523
    581524    /**
    582      * Processes the parsed nodes after parsing. Just adds them to
    583      * the dataset
    584      *
    585      */
    586     protected void processNodesAfterParsing() {
    587         for (OsmPrimitive primitive: externalIdMap.values()) {
    588             if (primitive instanceof Node) {
    589                 this.ds.addPrimitive(primitive);
    590             }
    591         }
    592     }
    593 
    594     /**
    595      * Processes the ways after parsing. Rebuilds the list of nodes of each way and
    596      * adds the way to the dataset
    597      *
    598      * @throws IllegalDataException thrown if a data integrity problem is detected
    599      */
    600     protected void processWaysAfterParsing() throws IllegalDataException{
    601         for (Long externalWayId: ways.keySet()) {
    602             Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
    603             List<Node> wayNodes = new ArrayList<Node>();
    604             for (long id : ways.get(externalWayId)) {
    605                 Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
    606                 if (n == null) {
    607                     if (id <= 0)
    608                         throw new IllegalDataException (
    609                                 tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
    610                                         externalWayId,
    611                                         id));
    612                     // create an incomplete node if necessary
    613                     //
    614                     n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE);
    615                     if (n == null) {
    616                         n = new Node(id);
    617                         ds.addPrimitive(n);
    618                     }
    619                 }
    620                 if (n.isDeleted()) {
    621                     System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId()));
    622                 } else {
    623                     wayNodes.add(n);
    624                 }
    625             }
    626             w.setNodes(wayNodes);
    627             if (w.hasIncompleteNodes()) {
    628                   System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.",
    629                           externalWayId, w.getNodesCount()));
    630             }
    631             ds.addPrimitive(w);
    632         }
    633     }
    634 
    635     /**
    636      * Completes the parsed relations with its members.
    637      *
    638      * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
    639      * relation member refers to a local primitive which wasn't available in the data
    640      *
    641      */
    642     private void processRelationsAfterParsing() throws IllegalDataException {
    643 
    644         // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
    645         for (Long externalRelationId : relations.keySet()) {
    646             Relation relation = (Relation) externalIdMap.get(
    647                     new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
    648             );
    649             ds.addPrimitive(relation);
    650         }
    651 
    652         for (Long externalRelationId : relations.keySet()) {
    653             Relation relation = (Relation) externalIdMap.get(
    654                     new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
    655             );
    656             List<RelationMember> relationMembers = new ArrayList<RelationMember>();
    657             for (RelationMemberData rm : relations.get(externalRelationId)) {
    658                 OsmPrimitive primitive = null;
    659 
    660                 // lookup the member from the map of already created primitives
    661                 primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type));
    662 
    663                 if (primitive == null) {
    664                     if (rm.id <= 0)
    665                         // relation member refers to a primitive with a negative id which was not
    666                         // found in the data. This is always a data integrity problem and we abort
    667                         // with an exception
    668                         //
    669                         throw new IllegalDataException(
    670                                 tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
    671                                         externalRelationId,
    672                                         rm.id));
    673 
    674                     // member refers to OSM primitive which was not present in the parsed data
    675                     // -> create a new incomplete primitive and add it to the dataset
    676                     //
    677                     primitive = ds.getPrimitiveById(rm.id, rm.type);
    678                     if (primitive == null) {
    679                         switch (rm.type) {
    680                         case NODE:
    681                             primitive = new Node(rm.id); break;
    682                         case WAY:
    683                             primitive = new Way(rm.id); break;
    684                         case RELATION:
    685                             primitive = new Relation(rm.id); break;
    686                         default: throw new AssertionError(); // can't happen
    687                         }
    688 
    689                         ds.addPrimitive(primitive);
    690                         externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive);
    691                     }
    692                 }
    693                 if (primitive.isDeleted()) {
    694                     System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId()));
    695                 } else {
    696                     relationMembers.add(new RelationMember(rm.role, primitive));
    697                 }
    698             }
    699             relation.setMembers(relationMembers);
    700         }
    701     }
    702 
    703     private void processChangesetAfterParsing() {
    704         if (uploadChangeset != null) {
    705             for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) {
    706                 ds.addChangeSetTag(e.getKey(), e.getValue());
    707             }
    708         }
    709     }
    710 
    711     /**
    712525     * Parse the given input source and return the dataset.
    713526     *
    714527     * @param source the source input stream. Must not be null.
     
    735548            progressMonitor.worked(1);
    736549
    737550            progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
    738             reader.ds.beginUpdate();
    739             try {
    740                 reader.processNodesAfterParsing();
    741                 reader.processWaysAfterParsing();
    742                 reader.processRelationsAfterParsing();
    743                 reader.processChangesetAfterParsing();
    744             } finally {
    745                 reader.ds.endUpdate();
    746             }
     551            reader.prepareDataSet();
    747552            progressMonitor.worked(1);
    748553            return reader.getDataSet();
    749554        } catch(IllegalDataException e) {
  • src/org/openstreetmap/josm/io/PbfImporter.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.io.File;
     7import java.io.InputStream;
     8
     9import org.openstreetmap.josm.actions.ExtensionFileFilter;
     10import org.openstreetmap.josm.data.osm.DataSet;
     11import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     12import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     13
     14/**
     15 * @author Vincent
     16 *
     17 */
     18public class PbfImporter extends OsmImporter {
     19   
     20    public PbfImporter() {
     21        super(new ExtensionFileFilter("osm.pbf", "osm.pbf", tr("OSM Server Files PBF compressed") + " (*.osm.pbf)"));
     22    }
     23
     24    /* (non-Javadoc)
     25     * @see org.openstreetmap.josm.io.OsmImporter#importData(java.io.InputStream, java.io.File)
     26     */
     27    @Override
     28    protected void importData(InputStream in, File associatedFile) throws IllegalDataException {
     29        final DataSet dataSet = PbfReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
     30        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
     31        addDataLayer(dataSet, layer, associatedFile.getPath());
     32    }
     33}
  • src/org/openstreetmap/josm/io/PbfReader.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.io.IOException;
     7import java.io.InputStream;
     8import java.util.ArrayList;
     9import java.util.Collection;
     10import java.util.Date;
     11import java.util.HashMap;
     12import java.util.List;
     13import java.util.Map;
     14
     15import org.openstreetmap.josm.data.coor.LatLon;
     16import org.openstreetmap.josm.data.osm.DataSet;
     17import org.openstreetmap.josm.data.osm.Node;
     18import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     19import org.openstreetmap.josm.data.osm.Relation;
     20import org.openstreetmap.josm.data.osm.User;
     21import org.openstreetmap.josm.data.osm.Way;
     22import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     23import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     24import org.openstreetmap.josm.tools.CheckParameterUtil;
     25
     26import crosby.binary.BinaryParser;
     27import crosby.binary.Osmformat;
     28import crosby.binary.Osmformat.DenseNodes;
     29import crosby.binary.Osmformat.HeaderBlock;
     30import crosby.binary.file.BlockInputStream;
     31import crosby.binary.file.FileBlockPosition;
     32
     33/**
     34 * @author Vincent
     35 *
     36 */
     37public class PbfReader extends AbstractReader {
     38   
     39    protected class PbfParser extends BinaryParser {
     40
     41        public IllegalDataException exception = null;
     42       
     43        @Override
     44        protected void parse(HeaderBlock header) {
     45        }
     46
     47        /* (non-Javadoc)
     48         * @see crosby.binary.BinaryParser#skipBlock(crosby.binary.file.FileBlockPosition)
     49         */
     50        @Override
     51        public boolean skipBlock(FileBlockPosition block) {
     52            return exception != null;
     53        }
     54       
     55        protected void checkCoordinates(LatLon coor) throws IllegalDataException {
     56            if (!coor.isValid()) {
     57                throw new IllegalDataException(tr("Invalid coordinates: {0}", coor));
     58            }
     59        }
     60
     61        protected void checkChangesetId(long id) throws IllegalDataException {
     62            if (id > Integer.MAX_VALUE) {
     63                throw new IllegalDataException(tr("Invalid changeset id: {0}", id));
     64            }
     65        }
     66       
     67        protected void checkTimestamp(long timestamp) throws IllegalDataException {
     68            if (timestamp < 0) {
     69                throw new IllegalDataException(tr("Invalid timestamp: {0}", timestamp));
     70            }
     71        }
     72
     73        @Override
     74        protected void parseDense(DenseNodes nodes) {
     75            if (exception == null) {
     76                try {
     77                    int keyIndex = 0;
     78                    // Almost all data is DELTA coded
     79                    long nodeId = 0;
     80                    long nodeLat = 0;
     81                    long nodeLon = 0;
     82                    long changesetId = 0;
     83                    int uid = 0;
     84                    int suid = 0;
     85                    long timestamp = 0;
     86                    for (int i = 0; i < nodes.getIdCount(); i++) {
     87                        // Id (delta) and version (normal)
     88                        Node node = new Node(nodeId+=nodes.getId(i), nodes.getDenseinfo().getVersion(i));
     89                        // Lat/Lon (delta)
     90                        node.setCoor(new LatLon(parseLat(nodeLat+=nodes.getLat(i)), parseLon(nodeLon+=nodes.getLon(i))));
     91                        checkCoordinates(node.getCoor());
     92                        // Changeset (delta)
     93                        checkChangesetId(changesetId+=nodes.getDenseinfo().getChangeset(i));
     94                        node.setChangesetId((int) changesetId);
     95                        // User (delta)
     96                        node.setUser(User.createOsmUser(uid+=nodes.getDenseinfo().getUid(i), getStringById(suid+=nodes.getDenseinfo().getUserSid(i))));
     97                        // Timestamp (delta)
     98                        checkTimestamp(timestamp+=nodes.getDenseinfo().getTimestamp(i));
     99                        node.setTimestamp(new Date(timestamp));
     100                        // A single table contains all keys/values of all nodes.
     101                        // Each node's tags are encoded in alternating <key_id> <value_id>.
     102                        // A single stringid of 0 delimit when the tags of a node ends and the tags of the next node begin.
     103                        Map<String, String> keys = new HashMap<String, String>();
     104                        while (keyIndex < nodes.getKeysValsCount()) {
     105                            int key_id = nodes.getKeysVals(keyIndex++);
     106                            if (key_id == 0) {
     107                                break; // End of current node's tags
     108                            } else if (keyIndex < nodes.getKeysValsCount()) {
     109                                int value_id = nodes.getKeysVals(keyIndex++);
     110                                keys.put(getStringById(key_id), getStringById(value_id));
     111                            } else {
     112                                throw new IllegalDataException(tr("Invalid DenseNodes key/values table"));
     113                            }
     114                        }
     115                        node.setKeys(keys);
     116                        externalIdMap.put(node.getPrimitiveId(), node);
     117                    }
     118                } catch (IllegalDataException e) {
     119                    exception = e;
     120                }
     121            }
     122        }
     123
     124        @Override
     125        protected void parseNodes(List<Osmformat.Node> osmNodes) {
     126            if (exception == null) {
     127                try {
     128                    for (Osmformat.Node n : osmNodes) {
     129                        Node node = new Node(n.getId(), n.getInfo().getVersion());
     130                        node.setCoor(new LatLon(parseLat(n.getLat()), parseLon(n.getLon())));
     131                        checkCoordinates(node.getCoor());
     132                        checkChangesetId(n.getInfo().getChangeset());
     133                        node.setChangesetId((int) n.getInfo().getChangeset());
     134                        node.setUser(User.createOsmUser(n.getInfo().getUid(), getStringById(n.getInfo().getUserSid())));
     135                        checkTimestamp(n.getInfo().getTimestamp());
     136                        node.setTimestamp(new Date(n.getInfo().getTimestamp()));
     137                        Map<String, String> keys = new HashMap<String, String>();
     138                        for (int i=0; i<n.getKeysCount(); i++) {
     139                            keys.put(getStringById(n.getKeys(i)), getStringById(n.getVals(i)));
     140                        }
     141                        node.setKeys(keys);
     142                        externalIdMap.put(node.getPrimitiveId(), node);
     143                    }
     144                } catch (IllegalDataException e) {
     145                    exception = e;
     146                }
     147            }
     148        }
     149       
     150        @Override
     151        protected void parseWays(List<Osmformat.Way> osmWays) {
     152            if (exception == null) {
     153                try {
     154                    for (Osmformat.Way w : osmWays) {
     155                        Way way = new Way(w.getId(), w.getInfo().getVersion());
     156                        checkChangesetId(w.getInfo().getChangeset());
     157                        way.setChangesetId((int) w.getInfo().getChangeset());
     158                        way.setUser(User.createOsmUser(w.getInfo().getUid(), getStringById(w.getInfo().getUserSid())));
     159                        checkTimestamp(w.getInfo().getTimestamp());
     160                        way.setTimestamp(new Date(w.getInfo().getTimestamp()));
     161                        Map<String, String> keys = new HashMap<String, String>();
     162                        for (int i=0; i<w.getKeysCount(); i++) {
     163                            keys.put(getStringById(w.getKeys(i)), getStringById(w.getVals(i)));
     164                        }
     165                        way.setKeys(keys);
     166                        long previousId = 0; // Node ids are delta coded
     167                        Collection<Long> nodeIds = new ArrayList<Long>();
     168                        for (Long id : w.getRefsList()) {
     169                            nodeIds.add(previousId+=id);
     170                        }
     171                        ways.put(way.getUniqueId(), nodeIds);
     172                        externalIdMap.put(way.getPrimitiveId(), way);
     173                    }
     174                } catch (IllegalDataException e) {
     175                    exception = e;
     176                }
     177            }
     178        }
     179       
     180        @Override
     181        protected void parseRelations(List<Osmformat.Relation> osmRels) {
     182            if (exception == null) {
     183                try {
     184                    for (Osmformat.Relation r : osmRels) {
     185                        Relation rel = new Relation(r.getId(), r.getInfo().getVersion());
     186                        checkChangesetId(r.getInfo().getChangeset());
     187                        rel.setChangesetId((int) r.getInfo().getChangeset());
     188                        rel.setUser(User.createOsmUser(r.getInfo().getUid(), getStringById(r.getInfo().getUserSid())));
     189                        checkTimestamp(r.getInfo().getTimestamp());
     190                        rel.setTimestamp(new Date(r.getInfo().getTimestamp()));
     191                        Map<String, String> keys = new HashMap<String, String>();
     192                        for (int i=0; i<r.getKeysCount(); i++) {
     193                            keys.put(getStringById(r.getKeys(i)), getStringById(r.getVals(i)));
     194                        }
     195                        rel.setKeys(keys);
     196                        long previousId = 0; // Member ids are delta coded
     197                        Collection<RelationMemberData> members = new ArrayList<RelationMemberData>();
     198                        for (int i = 0; i<r.getMemidsCount(); i++) {
     199                            RelationMemberData rmd = new RelationMemberData();
     200                            rmd.id = previousId+=r.getMemids(i);
     201                            rmd.role = getStringById(r.getRolesSid(i));
     202                            switch (r.getTypes(i)) {
     203                                case NODE:
     204                                    rmd.type = OsmPrimitiveType.NODE;
     205                                    break;
     206                                case WAY:
     207                                    rmd.type = OsmPrimitiveType.WAY;
     208                                    break;
     209                                case RELATION:
     210                                    rmd.type = OsmPrimitiveType.RELATION;
     211                                    break;
     212                            }
     213                            members.add(rmd);
     214                        }
     215                        relations.put(rel.getUniqueId(), members);
     216                        externalIdMap.put(rel.getPrimitiveId(), rel);
     217                    }
     218                } catch (IllegalDataException e) {
     219                    exception = e;
     220                }
     221            }
     222        }
     223
     224        @Override
     225        public void complete() {
     226        }
     227    }
     228
     229    private PbfParser parser = new PbfParser();
     230   
     231    /**
     232     * Parse the given input source and return the dataset.
     233     *
     234     * @param source the source input stream. Must not be null.
     235     * @param progressMonitor  the progress monitor. If null, {@see NullProgressMonitor#INSTANCE} is assumed
     236     *
     237     * @return the dataset with the parsed data
     238     * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
     239     * @throws IllegalArgumentException thrown if source is null
     240     */
     241    public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
     242        if (progressMonitor == null) {
     243            progressMonitor = NullProgressMonitor.INSTANCE;
     244        }
     245        CheckParameterUtil.ensureParameterNotNull(source, "source");
     246
     247        PbfReader reader = new PbfReader();
     248       
     249        try {
     250            progressMonitor.beginTask(tr("Prepare OSM data...", 2));
     251            progressMonitor.indeterminateSubTask(tr("Reading OSM data..."));
     252
     253            reader.parse(source);
     254            progressMonitor.worked(1);
     255
     256            progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
     257            reader.prepareDataSet();
     258            progressMonitor.worked(1);
     259            return reader.getDataSet();
     260        } catch (IllegalDataException e) {
     261            throw e;
     262        } catch (Exception e) {
     263            throw new IllegalDataException(e);
     264        } finally {
     265            progressMonitor.finishTask();
     266        }
     267    }
     268
     269    public void parse(InputStream source) throws IOException, IllegalDataException {
     270        new BlockInputStream(source, parser).process();
     271        if (parser.exception != null) {
     272            throw parser.exception;
     273        }
     274    }
     275}