예제 #1
0
 CodeSelectionPanel(String initialCode, ActionListener listener) {
   this.listener = listener;
   data = new ArrayList<>(Projections.getAllProjectionCodes());
   Collections.sort(data, new CodeComparator());
   filteredData = new ArrayList<>(data);
   build();
   setCode(initialCode != null ? initialCode : DEFAULT_CODE);
   selectionList.addListSelectionListener(this);
 }
예제 #2
0
 /** Setup test. */
 @Before
 public void setUp() {
   User.clearUserMap();
   my = new DataSet();
   my.setVersion("0.6");
   their = new DataSet();
   their.setVersion("0.6");
   Main.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
 }
예제 #3
0
  private void buildGUI() {
    GridBagConstraints c = new GridBagConstraints();
    c.gridheight = 1;
    c.gridwidth = 1;
    c.weightx = 1;
    c.weighty = 1;
    c.fill = GridBagConstraints.BOTH;

    this.projectionCombo = new JComboBox();
    this.projectionCombo.addItem(tr("Select projection..."));
    for (Projection p : Projections.getProjections()) {
      this.projectionCombo.addItem(p);
    }

    this.projectionPreferencesButton = new JButton(tr("Prefs"));
    updateProjectionPrefButton();

    this.loadFileButton = new JButton(tr("Load file..."));
    this.okButton = new JButton(tr("Place"));
    this.saveButton = new JButton(tr("Save"));
    this.showButton = new JButton(tr("Show target"));
    this.cancelButton = new JButton(tr("Discard"));
    this.loadProgress = new JProgressBar();
    this.progressRenderer = new LoadProgressRenderer(this.loadProgress);

    this.minXField = new JTextField("");
    this.minYField = new JTextField("");
    this.minEastField = new JTextField("");
    this.minNorthField = new JTextField("");
    this.getMinButton = new JButton(tr("Take X and Y from selected node"));

    this.maxXField = new JTextField("");
    this.maxYField = new JTextField("");
    this.maxEastField = new JTextField("");
    this.maxNorthField = new JTextField("");
    this.getMaxButton = new JButton(tr("Take X and Y from selected node"));

    this.debugModeCheck = new JCheckBox(tr("Debug info"));
    this.mergeCloseNodesCheck = new JCheckBox(tr("Merge close nodes"));
    this.mergeCloseNodesTolerance = new JTextField("1e-3");

    this.removeSmallObjectsCheck = new JCheckBox(tr("Remove objects smaller than"));
    this.removeSmallObjectsSize = new JTextField("1");

    this.removeLargeObjectsCheck = new JCheckBox(tr("Remove objects larger than"));
    this.removeLargeObjectsSize = new JTextField("10");

    this.colorFilterCheck = new JCheckBox(tr("Only this color"));
    this.colorFilterColor = new JTextField("#000000");

    this.removeParallelSegmentsCheck = new JCheckBox(tr("Remove parallel lines"));
    this.removeParallelSegmentsTolerance = new JTextField("3");

    this.limitPathCountCheck = new JCheckBox(tr("Take only first X paths"));
    this.limitPathCount = new JTextField("10000");

    this.splitOnColorChangeCheck = new JCheckBox(tr("Color/width change"));
    this.splitOnShapeClosedCheck = new JCheckBox(tr("Shape closed"));
    this.splitOnSingleSegmentCheck = new JCheckBox(tr("Single segments"));
    this.splitOnOrthogonalCheck = new JCheckBox(tr("Orthogonal shapes"));

    JPanel configPanel = new JPanel(new GridBagLayout());
    configPanel.setBorder(BorderFactory.createTitledBorder(tr("Import settings")));
    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 1;
    configPanel.add(this.mergeCloseNodesCheck, c);
    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHEAST;
    configPanel.add(new JLabel("Tolerance :"), c);
    c.gridx = 2;
    c.gridy = 0;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHWEST;
    configPanel.add(this.mergeCloseNodesTolerance, c);

    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 1;
    configPanel.add(this.removeSmallObjectsCheck, c);
    c.gridx = 1;
    c.gridy = 1;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHEAST;
    configPanel.add(new JLabel("Tolerance :"), c);
    c.gridx = 2;
    c.gridy = 1;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHWEST;
    configPanel.add(this.removeSmallObjectsSize, c);

    c.gridx = 0;
    c.gridy = 2;
    c.gridwidth = 1;
    configPanel.add(this.removeLargeObjectsCheck, c);
    c.gridx = 1;
    c.gridy = 2;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHEAST;
    configPanel.add(new JLabel("Tolerance :"), c);
    c.gridx = 2;
    c.gridy = 2;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHWEST;
    configPanel.add(this.removeLargeObjectsSize, c);

    c.gridx = 0;
    c.gridy = 3;
    c.gridwidth = 1;
    configPanel.add(this.removeParallelSegmentsCheck, c);
    c.gridx = 1;
    c.gridy = 3;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHEAST;
    configPanel.add(new JLabel("Max distance :"), c);
    c.gridx = 2;
    c.gridy = 3;
    c.gridwidth = 1;
    c.anchor = GridBagConstraints.NORTHWEST;
    configPanel.add(this.removeParallelSegmentsTolerance, c);

    c.gridx = 0;
    c.gridy = 4;
    c.gridwidth = 2;
    configPanel.add(this.limitPathCountCheck, c);
    c.gridx = 2;
    c.gridy = 4;
    c.gridwidth = 1;
    configPanel.add(this.limitPathCount, c);

    c.gridx = 0;
    c.gridy = 5;
    c.gridwidth = 1;
    configPanel.add(this.colorFilterCheck, c);
    c.gridx = 2;
    c.gridy = 5;
    c.gridwidth = 1;
    configPanel.add(this.colorFilterColor, c);

    c.gridx = 0;
    c.gridy = 6;
    c.gridwidth = 2;
    configPanel.add(this.debugModeCheck, c);

    c.gridx = 0;
    c.gridy = 7;
    c.gridwidth = 1;
    configPanel.add(new JLabel(tr("Introduce separate layers for:")), c);
    c.gridx = 1;
    c.gridy = 7;
    c.gridwidth = 1;
    configPanel.add(this.splitOnShapeClosedCheck, c);
    c.gridx = 2;
    c.gridy = 7;
    c.gridwidth = 1;
    configPanel.add(this.splitOnSingleSegmentCheck, c);
    c.gridx = 1;
    c.gridy = 8;
    c.gridwidth = 1;
    configPanel.add(this.splitOnColorChangeCheck, c);
    c.gridx = 2;
    c.gridy = 8;
    c.gridwidth = 1;
    configPanel.add(this.splitOnOrthogonalCheck, c);

    JPanel projectionPanel = new JPanel(new GridBagLayout());
    projectionPanel.setBorder(BorderFactory.createTitledBorder(tr("Bind to coordinates")));

    JPanel projectionSubPanel = new JPanel();
    projectionSubPanel.setLayout(new BoxLayout(projectionSubPanel, BoxLayout.X_AXIS));

    projectionSubPanel.add(new JLabel(tr("Projection:")));
    projectionSubPanel.add(this.projectionCombo);
    projectionSubPanel.add(this.projectionPreferencesButton);
    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 3;
    projectionPanel.add(projectionSubPanel, c);

    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 2;
    projectionPanel.add(new JLabel(tr("Bottom left (min) corner:")), c);
    c.gridx = 0;
    c.gridy = 2;
    c.gridwidth = 1;
    projectionPanel.add(new JLabel(tr("PDF X and Y")), c);
    c.gridx = 1;
    c.gridy = 2;
    c.gridwidth = 1;
    projectionPanel.add(new JLabel(tr("East and North")), c);
    c.gridx = 0;
    c.gridy = 3;
    c.gridwidth = 1;
    projectionPanel.add(this.minXField, c);
    c.gridx = 0;
    c.gridy = 4;
    c.gridwidth = 1;
    projectionPanel.add(this.minYField, c);
    c.gridx = 1;
    c.gridy = 3;
    c.gridwidth = 1;
    projectionPanel.add(this.minEastField, c);
    c.gridx = 1;
    c.gridy = 4;
    c.gridwidth = 1;
    projectionPanel.add(this.minNorthField, c);
    c.gridx = 0;
    c.gridy = 5;
    c.gridwidth = 1;
    projectionPanel.add(this.getMinButton, c);

    c.gridx = 0;
    c.gridy = 6;
    c.gridwidth = 2;
    projectionPanel.add(new JLabel(tr("Top right (max) corner:")), c);
    c.gridx = 0;
    c.gridy = 7;
    c.gridwidth = 1;
    projectionPanel.add(new JLabel(tr("PDF X and Y")), c);
    c.gridx = 1;
    c.gridy = 7;
    c.gridwidth = 1;
    projectionPanel.add(new JLabel(tr("East and North")), c);
    c.gridx = 0;
    c.gridy = 8;
    c.gridwidth = 1;
    projectionPanel.add(this.maxXField, c);
    c.gridx = 0;
    c.gridy = 9;
    c.gridwidth = 1;
    projectionPanel.add(this.maxYField, c);
    c.gridx = 1;
    c.gridy = 8;
    c.gridwidth = 1;
    projectionPanel.add(this.maxEastField, c);
    c.gridx = 1;
    c.gridy = 9;
    c.gridwidth = 1;
    projectionPanel.add(this.maxNorthField, c);
    c.gridx = 0;
    c.gridy = 10;
    c.gridwidth = 1;
    projectionPanel.add(this.getMaxButton, c);

    JPanel okCancelPanel = new JPanel(new GridLayout(1, 3));
    okCancelPanel.add(this.cancelButton);
    okCancelPanel.add(this.showButton);
    okCancelPanel.add(this.okButton);
    okCancelPanel.add(this.saveButton);

    JPanel panel = new JPanel(new GridBagLayout());
    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 1;
    panel.add(configPanel, c);
    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 1;
    panel.add(loadFileButton, c);
    c.gridx = 0;
    c.gridy = 2;
    c.gridwidth = 1;
    panel.add(projectionPanel, c);
    c.gridx = 0;
    c.gridy = 3;
    c.gridwidth = 1;
    panel.add(okCancelPanel, c);
    c.gridx = 0;
    c.gridy = 4;
    c.gridwidth = 1;
    panel.add(this.loadProgress, c);

    this.setSize(450, 550);
    this.setContentPane(panel);
  }
예제 #4
0
 @Override
 public Projection getProjection() {
   return Projections.getProjectionByCode(code);
 }
예제 #5
0
  private void parseJos(Document doc, ProgressMonitor progressMonitor) throws IllegalDataException {
    Element root = doc.getDocumentElement();
    if (!equal(root.getTagName(), "josm-session")) {
      error(tr("Unexpected root element ''{0}'' in session file", root.getTagName()));
    }
    String version = root.getAttribute("version");
    if (!"0.1".equals(version)) {
      error(tr("Version ''{0}'' of session file is not supported. Expected: 0.1", version));
    }

    Element viewportEl = getElementByTagName(root, "viewport");
    if (viewportEl != null) {
      EastNorth center = null;
      Element centerEl = getElementByTagName(viewportEl, "center");
      if (centerEl != null && centerEl.hasAttribute("lat") && centerEl.hasAttribute("lon")) {
        try {
          LatLon centerLL =
              new LatLon(
                  Double.parseDouble(centerEl.getAttribute("lat")),
                  Double.parseDouble(centerEl.getAttribute("lon")));
          center = Projections.project(centerLL);
        } catch (NumberFormatException ex) {
        }
      }
      if (center != null) {
        Element scaleEl = getElementByTagName(viewportEl, "scale");
        if (scaleEl != null && scaleEl.hasAttribute("meter-per-pixel")) {
          try {
            double meterPerPixel = Double.parseDouble(scaleEl.getAttribute("meter-per-pixel"));
            Projection proj = Main.getProjection();
            // Get a "typical" distance in east/north units that
            // corresponds to a couple of pixels. Shouldn't be too
            // large, to keep it within projection bounds and
            // not too small to avoid rounding errors.
            double dist = 0.01 * proj.getDefaultZoomInPPD();
            LatLon ll1 = proj.eastNorth2latlon(new EastNorth(center.east() - dist, center.north()));
            LatLon ll2 = proj.eastNorth2latlon(new EastNorth(center.east() + dist, center.north()));
            double meterPerEasting = ll1.greatCircleDistance(ll2) / dist / 2;
            double scale = meterPerPixel / meterPerEasting; // unit: easting per pixel
            viewport = new ViewportData(center, scale);
          } catch (NumberFormatException ex) {
          }
        }
      }
    }

    Element layersEl = getElementByTagName(root, "layers");
    if (layersEl == null) return;

    String activeAtt = layersEl.getAttribute("active");
    try {
      active = (activeAtt != null && !activeAtt.isEmpty()) ? Integer.parseInt(activeAtt) - 1 : -1;
    } catch (NumberFormatException e) {
      Main.warn(
          "Unsupported value for 'active' layer attribute. Ignoring it. Error was: "
              + e.getMessage());
      active = -1;
    }

    MultiMap<Integer, Integer> deps = new MultiMap<Integer, Integer>();
    Map<Integer, Element> elems = new HashMap<Integer, Element>();

    NodeList nodes = layersEl.getChildNodes();

    for (int i = 0; i < nodes.getLength(); ++i) {
      Node node = nodes.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
        Element e = (Element) node;
        if (equal(e.getTagName(), "layer")) {

          if (!e.hasAttribute("index")) {
            error(tr("missing mandatory attribute ''index'' for element ''layer''"));
          }
          Integer idx = null;
          try {
            idx = Integer.parseInt(e.getAttribute("index"));
          } catch (NumberFormatException ex) {
          }
          if (idx == null) {
            error(tr("unexpected format of attribute ''index'' for element ''layer''"));
          }
          if (elems.containsKey(idx)) {
            error(
                tr(
                    "attribute ''index'' ({0}) for element ''layer'' must be unique",
                    Integer.toString(idx)));
          }
          elems.put(idx, e);

          deps.putVoid(idx);
          String depStr = e.getAttribute("depends");
          if (depStr != null) {
            for (String sd : depStr.split(",")) {
              Integer d = null;
              try {
                d = Integer.parseInt(sd);
              } catch (NumberFormatException ex) {
                Main.warn(ex);
              }
              if (d != null) {
                deps.put(idx, d);
              }
            }
          }
        }
      }
    }

    List<Integer> sorted = Utils.topologicalSort(deps);
    final Map<Integer, Layer> layersMap = new TreeMap<Integer, Layer>(Collections.reverseOrder());
    final Map<Integer, SessionLayerImporter> importers =
        new HashMap<Integer, SessionLayerImporter>();
    final Map<Integer, String> names = new HashMap<Integer, String>();

    progressMonitor.setTicksCount(sorted.size());
    LAYER:
    for (int idx : sorted) {
      Element e = elems.get(idx);
      if (e == null) {
        error(tr("missing layer with index {0}", idx));
      }
      if (!e.hasAttribute("name")) {
        error(tr("missing mandatory attribute ''name'' for element ''layer''"));
      }
      String name = e.getAttribute("name");
      names.put(idx, name);
      if (!e.hasAttribute("type")) {
        error(tr("missing mandatory attribute ''type'' for element ''layer''"));
      }
      String type = e.getAttribute("type");
      SessionLayerImporter imp = getSessionLayerImporter(type);
      if (imp == null) {
        CancelOrContinueDialog dialog = new CancelOrContinueDialog();
        dialog.show(
            tr("Unable to load layer"),
            tr("Cannot load layer of type ''{0}'' because no suitable importer was found.", type),
            JOptionPane.WARNING_MESSAGE,
            progressMonitor);
        if (dialog.isCancel()) {
          progressMonitor.cancel();
          return;
        } else {
          continue;
        }
      } else {
        importers.put(idx, imp);
        List<LayerDependency> depsImp = new ArrayList<LayerDependency>();
        for (int d : deps.get(idx)) {
          SessionLayerImporter dImp = importers.get(d);
          if (dImp == null) {
            CancelOrContinueDialog dialog = new CancelOrContinueDialog();
            dialog.show(
                tr("Unable to load layer"),
                tr(
                    "Cannot load layer {0} because it depends on layer {1} which has been skipped.",
                    idx, d),
                JOptionPane.WARNING_MESSAGE,
                progressMonitor);
            if (dialog.isCancel()) {
              progressMonitor.cancel();
              return;
            } else {
              continue LAYER;
            }
          }
          depsImp.add(new LayerDependency(d, layersMap.get(d), dImp));
        }
        ImportSupport support = new ImportSupport(name, idx, depsImp);
        Layer layer = null;
        Exception exception = null;
        try {
          layer = imp.load(e, support, progressMonitor.createSubTaskMonitor(1, false));
        } catch (IllegalDataException ex) {
          exception = ex;
        } catch (IOException ex) {
          exception = ex;
        }
        if (exception != null) {
          exception.printStackTrace();
          CancelOrContinueDialog dialog = new CancelOrContinueDialog();
          dialog.show(
              tr("Error loading layer"),
              tr(
                  "<html>Could not load layer {0} ''{1}''.<br>Error is:<br>{2}</html>",
                  idx, name, exception.getMessage()),
              JOptionPane.ERROR_MESSAGE,
              progressMonitor);
          if (dialog.isCancel()) {
            progressMonitor.cancel();
            return;
          } else {
            continue;
          }
        }

        if (layer == null) throw new RuntimeException();
        layersMap.put(idx, layer);
      }
      progressMonitor.worked(1);
    }

    layers = new ArrayList<Layer>();
    for (int idx : layersMap.keySet()) {
      Layer layer = layersMap.get(idx);
      if (layer == null) {
        continue;
      }
      Element el = elems.get(idx);
      if (el.hasAttribute("visible")) {
        layer.setVisible(Boolean.parseBoolean(el.getAttribute("visible")));
      }
      if (el.hasAttribute("opacity")) {
        try {
          double opacity = Double.parseDouble(el.getAttribute("opacity"));
          layer.setOpacity(opacity);
        } catch (NumberFormatException ex) {
          Main.warn(ex);
        }
      }
    }
    for (Entry<Integer, Layer> e : layersMap.entrySet()) {
      Layer l = e.getValue();
      if (l == null) {
        continue;
      }

      l.setName(names.get(e.getKey()));
      layers.add(l);
    }
  }
예제 #6
0
/** @author ak */
public class SvgImportTask extends PleaseWaitRunnable {
  LinkedList<Node> nodes = new LinkedList<>();
  LinkedList<Way> ways = new LinkedList<>();
  private List<File> files;
  private boolean canceled;

  public SvgImportTask(List<File> files) {
    super(I18n.tr("Importing..."), false);
    this.files = new ArrayList<>(files);
  }

  @Override
  protected void cancel() {
    this.canceled = true;
  }

  @Override
  protected void finish() {}

  Projection projection = Projections.getProjectionByCode("EPSG:3857"); // Mercator
  EastNorth center;
  double scale;
  Way currentway;
  double lastX;
  double lastY;

  private void appendNode(double x, double y) throws IOException {
    if (currentway == null) {
      throw new IOException("Shape is started incorectly");
    }
    Node nd = new Node(projection.eastNorth2latlon(center.add(x * scale, -y * scale)));
    if (nd.getCoor().isOutSideWorld()) {
      throw new IOException("Shape goes outside the world");
    }
    currentway.addNode(nd);
    nodes.add(nd);
    lastX = x;
    lastY = y;
  }

  private void appendNode(Point2D point) throws IOException {
    appendNode(point.getX(), point.getY());
  }

  private static double sqr(double x) {
    return x * x;
  }

  private static double cube(double x) {
    return x * x * x;
  }

  private static Point2D interpolate_quad(
      double ax, double ay, double bx, double by, double cx, double cy, double t) {
    return new Point2D.Double(
        sqr(1 - t) * ax + 2 * (1 - t) * t * bx + t * t * cx,
        sqr(1 - t) * ay + 2 * (1 - t) * t * by + t * t * cy);
  }

  private static Point2D interpolate_cubic(
      double ax,
      double ay,
      double bx,
      double by,
      double cx,
      double cy,
      double dx,
      double dy,
      double t) {
    return new Point2D.Double(
        cube(1 - t) * ax + 3 * sqr(1 - t) * t * bx + 3 * (1 - t) * t * t * cx + t * t * t * dx,
        cube(1 - t) * ay + 3 * sqr(1 - t) * t * by + 3 * (1 - t) * t * t * cy + t * t * t * dy);
  }

  private void processElement(SVGElement el, AffineTransform transform) throws IOException {
    if (el instanceof Group) {
      AffineTransform oldTransform = transform;
      AffineTransform xform = ((Group) el).getXForm();
      if (transform == null) {
        transform = xform;
      } else if (xform != null) {
        transform = new AffineTransform(transform);
        transform.concatenate(xform);
      }
      for (Object child : ((Group) el).getChildren(null)) {
        processElement((SVGElement) child, transform);
      }
      transform = oldTransform;
    } else if (el instanceof ShapeElement) {
      Shape shape = ((ShapeElement) el).getShape();
      if (transform != null) {
        shape = transform.createTransformedShape(shape);
      }
      PathIterator it = shape.getPathIterator(null);
      while (!it.isDone()) {
        double[] coords = new double[6];
        switch (it.currentSegment(coords)) {
          case PathIterator.SEG_MOVETO:
            currentway = new Way();
            ways.add(currentway);
            appendNode(coords[0], coords[1]);
            break;
          case PathIterator.SEG_LINETO:
            appendNode(coords[0], coords[1]);
            break;
          case PathIterator.SEG_CLOSE:
            if (currentway.firstNode().getCoor().equalsEpsilon(nodes.getLast().getCoor())) {
              currentway.removeNode(nodes.removeLast());
            }
            currentway.addNode(currentway.firstNode());
            break;
          case PathIterator.SEG_QUADTO:
            double lastx = lastX;
            double lasty = lastY;
            for (int i = 1; i < Settings.getCurveSteps(); i++) {
              appendNode(
                  interpolate_quad(
                      lastx,
                      lasty,
                      coords[0],
                      coords[1],
                      coords[2],
                      coords[3],
                      i / Settings.getCurveSteps()));
            }
            appendNode(coords[2], coords[3]);
            break;
          case PathIterator.SEG_CUBICTO:
            lastx = lastX;
            lasty = lastY;
            for (int i = 1; i < Settings.getCurveSteps(); i++) {
              appendNode(
                  interpolate_cubic(
                      lastx,
                      lasty,
                      coords[0],
                      coords[1],
                      coords[2],
                      coords[3],
                      coords[4],
                      coords[5],
                      i / Settings.getCurveSteps()));
            }
            appendNode(coords[4], coords[5]);
            break;
        }
        it.next();
      }
    }
  }

  @Override
  protected void realRun() throws IOException, OsmTransferException {
    LatLon center = Main.getProjection().eastNorth2latlon(Main.map.mapView.getCenter());
    scale =
        Settings.getScaleNumerator()
            / Settings.getScaleDivisor()
            / Math.cos(Math.toRadians(center.lat()));
    this.center = projection.latlon2eastNorth(center);
    try {
      SVGUniverse universe = new SVGUniverse();
      universe.setVerbose(Main.pref.getBoolean("importvec.verbose", false));
      for (File f : files) {
        if (f.isDirectory()) continue;
        if (canceled) {
          return;
        }
        SVGDiagram diagram = universe.getDiagram(f.toURI());
        ShapeElement root = diagram.getRoot();
        if (root == null) {
          throw new IOException("Can't find root SVG element");
        }
        Rectangle2D bbox = root.getBoundingBox();
        this.center = this.center.add(-bbox.getCenterX() * scale, bbox.getCenterY() * scale);
        processElement(root, null);
      }
    } catch (IOException e) {
      throw e;
    } catch (Exception e) {
      throw new IOException(e);
    }
    LinkedList<Command> cmds = new LinkedList<>();
    for (Node n : nodes) {
      cmds.add(new AddCommand(n));
    }
    for (Way w : ways) {
      cmds.add(new AddCommand(w));
    }
    Main.main.undoRedo.add(new SequenceCommand("Import primitives", cmds));
  }
}