Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for Mapillary point clouds #229

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.openstreetmap.josm.plugins.mapillary.actions.MapPointObjectLayerAction;
import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryDownloadAction;
import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryExportAction;
import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryPointCloudAction;
import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryZoomAction;
import org.openstreetmap.josm.plugins.mapillary.data.mapillary.VectorDataSelectionListener;
import org.openstreetmap.josm.plugins.mapillary.data.mapillary.smartedit.IgnoredObjects;
Expand Down Expand Up @@ -104,6 +105,10 @@ public MapillaryPlugin(PluginInformation info) {
mapPointObjectLayerAction.updateEnabledState();
destroyables.add(mapPointObjectLayerAction);

MapillaryPointCloudAction mapillaryPointCloudAction = new MapillaryPointCloudAction();
MainMenu.add(menu.imagerySubMenu, mapillaryPointCloudAction, true); // FIXME expert until stable
destroyables.add(mapillaryPointCloudAction);

// TODO remove in destroy (not currently possible)
RequestProcessor.addRequestHandlerClass("photo", MapillaryRemoteControl.class);
RequestProcessor.addRequestHandlerClass("mapillaryfilter", MapillaryFilterRemoteControl.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.actions;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;

import javax.swing.JOptionPane;

import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.gui.Notification;
import org.openstreetmap.josm.plugins.mapillary.MapillaryPlugin;
import org.openstreetmap.josm.plugins.mapillary.gui.layer.MapillaryLayer;
import org.openstreetmap.josm.plugins.mapillary.gui.layer.pointcloud.MapillaryPointCloudLayer;
import org.openstreetmap.josm.plugins.mapillary.oauth.OAuthUtils;
import org.openstreetmap.josm.plugins.mapillary.spi.preferences.MapillaryConfig;
import org.openstreetmap.josm.plugins.mapillary.utils.MapillaryImageUtils;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.Shortcut;

/**
* An action to add point clouds as aerial imagery
*/
public class MapillaryPointCloudAction extends JosmAction {
/**
* Create a new action
*/
public MapillaryPointCloudAction() {
super(tr("Mapillary Point Cloud (experimental)"),
new ImageProvider(MapillaryPlugin.LOGO).setSize(ImageProvider.ImageSizes.DEFAULT),
tr("Open Mapillary Point Cloud layer"), Shortcut.registerShortcut("mapillary:pointcloud",
tr("Mapillary Point Cloud"), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
false, "mapillary:pointcloud", true);
}

@Override
public void actionPerformed(ActionEvent e) {
final var selected = MapillaryLayer.getInstance().getImage();
try {
var obj = OAuthUtils
.getWithHeader(URI.create(MapillaryConfig.getUrls().getImageInformation(selected.getUniqueId(),
MapillaryImageUtils.ImageProperties.SFM_CLUSTER)))
.getJsonObject(MapillaryImageUtils.ImageProperties.SFM_CLUSTER.toString());
if (obj.containsKey("id") && obj.containsKey("url")) {
MainApplication.getLayerManager()
.addLayer(new MapillaryPointCloudLayer(obj.getString("id"), obj.getString("url"), selected));
} else {
new Notification(tr("Could not find point cloud for Mapillary image {0}", selected.getUniqueId()))
.setIcon(JOptionPane.ERROR_MESSAGE).show();
}
} catch (IOException ioException) {
throw new UncheckedIOException(ioException);
}
}

@Override
protected void updateEnabledState() {
setEnabled(MapillaryLayer.hasInstance() && MapillaryLayer.getInstance().getImage() != null || true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

public record PointCloudCamera(ProjectionType projectionType, int width, int height, double focal, double k1,
double k2) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

public record PointCloudColor(int r, int g, int b) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

import org.openstreetmap.josm.data.coor.ILatLon;

public record PointCloudLatLonAlt(double lat, double lon, double altitude) implements ILatLon {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

public record PointCloudPoint(PointCloudXYZ coordinates, PointCloudColor color) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

import java.util.Map;

public record PointCloudReconstruction(PointCloudLatLonAlt referenceLatLonAlt, Map<String, PointCloudCamera> cameras,
Map<String, PointCloudShot> shots, Map<String, PointCloudPoint> points) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

import java.time.Instant;

public record PointCloudShot(String cameraId, PointCloudXYZ rotation, PointCloudXYZ translation,
PointCloudXYZ gpsPosition, double gpsDop, int orientation, Instant captureTime) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

public record PointCloudXYZ(double x, double y, double z) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.data.mapillary.pointcloud;

public enum ProjectionType {
PERSPECTIVE, BROWN, FISHEYE, EQUIRECTANGULAR
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapillary.gui.layer.pointcloud;

import static org.openstreetmap.gui.jmapviewer.OsmMercator.MERCATOR_256;

import java.awt.Point;

import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.OsmMercator;
import org.openstreetmap.gui.jmapviewer.Projected;
import org.openstreetmap.gui.jmapviewer.Tile;
import org.openstreetmap.gui.jmapviewer.TileRange;
import org.openstreetmap.gui.jmapviewer.TileXY;
import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
import org.openstreetmap.gui.jmapviewer.interfaces.IProjected;
import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource;
import org.openstreetmap.josm.data.coor.ILatLon;
import org.openstreetmap.josm.data.imagery.ImageryInfo;

/**
* The source for creating tiles for Mapillary Point Clouds
*/
public class MapillaryPointCloudImageSource extends AbstractTMSTileSource {

private final ILatLon origin;

public MapillaryPointCloudImageSource(ImageryInfo info, ILatLon origin) {
super(info);
this.origin = origin;
}

@Override
public double getDistance(double lat1, double lon1, double lat2, double lon2) {
return osmMercator().getDistance(lat1, lon1, lat2, lon2);
}

@Override
public Point latLonToXY(double lat, double lon, int zoom) {
return new Point((int) Math.round(osmMercator().lonToX(lon, zoom)),
(int) Math.round(osmMercator().latToY(lat, zoom)));
}

@Override
public ICoordinate xyToLatLon(int x, int y, int zoom) {
return new Coordinate(osmMercator().yToLat(y, zoom), osmMercator().xToLon(x, zoom));
}

@Override
public TileXY latLonToTileXY(double lat, double lon, int zoom) {
return new TileXY(osmMercator().lonToX(lon, zoom) / getTileSize(),
osmMercator().latToY(lat, zoom) / getTileSize());
}

@Override
public ICoordinate tileXYToLatLon(int x, int y, int zoom) {
return new Coordinate(osmMercator().yToLat((long) y * getTileSize(), zoom),
osmMercator().xToLon((long) x * getTileSize(), zoom));
}

@Override
public IProjected tileXYtoProjected(int x, int y, int zoom) {
final var mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS;
final var f = mercatorWidth * getTileSize() / osmMercator().getMaxPixels(zoom);
return new Projected(f * x - mercatorWidth / 2, -(f * y - mercatorWidth / 2));
}

@Override
public TileXY projectedToTileXY(IProjected p, int zoom) {
final var mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS;
final var f = mercatorWidth * getTileSize() / osmMercator().getMaxPixels(zoom);
return new TileXY((p.getEast() + mercatorWidth / 2) / f, (-p.getNorth() + mercatorWidth / 2) / f);
}

@Override
public boolean isInside(Tile inner, Tile outer) {
final int dz = inner.getZoom() - outer.getZoom();
if (dz < 0)
return false;
return outer.getXtile() == inner.getXtile() >> dz && outer.getYtile() == inner.getYtile() >> dz;
}

@Override
public TileRange getCoveringTileRange(Tile tile, int newZoom) {
if (newZoom <= tile.getZoom()) {
final int dz = tile.getZoom() - newZoom;
final var xy = new TileXY(tile.getXtile() >> dz, tile.getYtile() >> dz);
return new TileRange(xy, xy, newZoom);
} else {
final int dz = newZoom - tile.getZoom();
final var t1 = new TileXY(tile.getXtile() << dz, tile.getYtile() << dz);
final var t2 = new TileXY(t1.getX() + (1 << dz) - 1, t1.getY() + (1 << dz) - 1);
return new TileRange(t1, t2, newZoom);
}
}

@Override
public String getServerCRS() {
return "EPSG:3857";
}

private static OsmMercator osmMercator() {
return MERCATOR_256;
}
}
Loading
Loading