Layer list fix, api docs update, unit test fix, map type button switch improvements

pull/237/head
Piero Toffanin 2017-07-13 17:06:25 -04:00
rodzic f7fa29cd3c
commit ab0b91a77e
5 zmienionych plików z 92 dodań i 38 usunięć

Wyświetl plik

@ -31,8 +31,8 @@ class MapView extends React.Component {
} }
getTilesByMapType(type){ getTilesByMapType(type){
// Go through the list of tiles and return // Go through the list of map items and return
// only those that match a particular type // only those that match a particular type (in tile format)
const tiles = []; const tiles = [];
this.props.mapItems.forEach(mapItem => { this.props.mapItems.forEach(mapItem => {
@ -64,7 +64,7 @@ class MapView extends React.Component {
render(){ render(){
const { opacity } = this.state; const { opacity } = this.state;
const mapTypeButtons = [ let mapTypeButtons = [
{ {
label: "Orthophoto", label: "Orthophoto",
type: "orthophoto" type: "orthophoto"
@ -77,7 +77,10 @@ class MapView extends React.Component {
label: "Terrain Model", label: "Terrain Model",
type: "dtm" type: "dtm"
} }
]; ].filter(mapType => this.getTilesByMapType(mapType.type).length > 0 );
// If we have only one button, hide it...
if (mapTypeButtons.length === 1) mapTypeButtons = [];
return (<div className="map-view"> return (<div className="map-view">
<div className="map-type-selector btn-group" role="group"> <div className="map-type-selector btn-group" role="group">

Wyświetl plik

@ -45,18 +45,31 @@ class Map extends React.Component {
this.imageryLayers = []; this.imageryLayers = [];
this.basemaps = {}; this.basemaps = {};
this.mapBounds = null; this.mapBounds = null;
this.autolayers = null;
this.loadImageryLayers = this.loadImageryLayers.bind(this); this.loadImageryLayers = this.loadImageryLayers.bind(this);
} }
loadImageryLayers(){ loadImageryLayers(forceAddLayers = false){
const { tiles } = this.props, const { tiles } = this.props,
assets = AssetDownloads.excludeSeparators(); assets = AssetDownloads.excludeSeparators(),
layerId = layer => {
const meta = layer[Symbol.for("meta")];
return meta.project + "_" + meta.task;
};
// Remove all previous imagery layers // Remove all previous imagery layers
this.imageryLayers.forEach(layer => layer.remove()); // and keep track of which ones were selected
const prevSelectedLayers = [];
this.imageryLayers.forEach(layer => {
this.autolayers.removeLayer(layer);
if (this.map.hasLayer(layer)) prevSelectedLayers.push(layerId(layer));
layer.remove();
});
this.imageryLayers = []; this.imageryLayers = [];
// Request new tiles
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.tileJsonRequests = []; this.tileJsonRequests = [];
@ -74,12 +87,16 @@ class Map extends React.Component {
maxZoom: info.maxzoom, maxZoom: info.maxzoom,
tms: info.scheme === 'tms', tms: info.scheme === 'tms',
opacity: this.props.opacity / 100 opacity: this.props.opacity / 100
}).addTo(this.map); });
// Associate metadata with this layer // Associate metadata with this layer
meta.name = info.name; meta.name = info.name;
layer[Symbol.for("meta")] = meta; layer[Symbol.for("meta")] = meta;
if (forceAddLayers || prevSelectedLayers.indexOf(layerId(layer)) !== -1){
layer.addTo(this.map);
}
// Show 3D switch button only if we have a single orthophoto // Show 3D switch button only if we have a single orthophoto
const task = { const task = {
id: meta.task, id: meta.task,
@ -118,6 +135,9 @@ class Map extends React.Component {
mapBounds.extend(bounds); mapBounds.extend(bounds);
this.mapBounds = mapBounds; this.mapBounds = mapBounds;
// Add layer to layers control
this.autolayers.addOverlay(layer, info.name);
done(); done();
}) })
.fail((_, __, err) => done(err)) .fail((_, __, err) => done(err))
@ -138,10 +158,17 @@ class Map extends React.Component {
this.map = Leaflet.map(this.container, { this.map = Leaflet.map(this.container, {
scrollWheelZoom: true, scrollWheelZoom: true,
measureControl: true,
positionControl: true positionControl: true
}); });
const measureControl = Leaflet.control.measure({
primaryLengthUnit: 'meters',
secondaryLengthUnit: 'feet',
primaryAreaUnit: 'sqmeters',
secondaryAreaUnit: 'acres'
});
measureControl.addTo(this.map);
if (showBackground) { if (showBackground) {
this.basemaps = { this.basemaps = {
"Google Maps Hybrid": L.tileLayer('//{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { "Google Maps Hybrid": L.tileLayer('//{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
@ -166,6 +193,12 @@ class Map extends React.Component {
}; };
} }
this.autolayers = Leaflet.control.autolayers({
overlays: {},
selectedOverlays: [],
baseLayers: this.basemaps
}).addTo(this.map);
this.map.fitWorld(); this.map.fitWorld();
Leaflet.control.scale({ Leaflet.control.scale({
@ -173,22 +206,9 @@ class Map extends React.Component {
}).addTo(this.map); }).addTo(this.map);
this.map.attributionControl.setPrefix(""); this.map.attributionControl.setPrefix("");
this.loadImageryLayers().then(() => { this.loadImageryLayers(true).then(() => {
this.map.fitBounds(this.mapBounds); this.map.fitBounds(this.mapBounds);
// Add basemaps / layers control
let overlays = {};
this.imageryLayers.forEach(layer => {
const meta = layer[Symbol.for("meta")];
overlays[meta.name] = layer;
});
Leaflet.control.autolayers({
overlays: overlays,
selectedOverlays: [],
baseLayers: this.basemaps
}).addTo(this.map);
this.map.on('click', e => { this.map.on('click', e => {
// Find first tile layer at the selected coordinates // Find first tile layer at the selected coordinates
for (let layer of this.imageryLayers){ for (let layer of this.imageryLayers){
@ -199,7 +219,6 @@ class Map extends React.Component {
} }
}); });
}); });
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -208,7 +227,9 @@ class Map extends React.Component {
}); });
if (prevProps.tiles !== this.props.tiles){ if (prevProps.tiles !== this.props.tiles){
this.loadImageryLayers(); this.loadImageryLayers().then(() => {
// console.log("GOT: ", this.autolayers, this.autolayers.selectedOverlays);
});
} }
} }

Wyświetl plik

@ -156,20 +156,22 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(task.processing_node is None) self.assertTrue(task.processing_node is None)
# tiles.json should not be accessible at this point # tiles.json should not be accessible at this point
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(project.id, task.id)) tile_types = ['orthophoto', 'dsm', 'dtm']
self.assertTrue(res.status_code == status.HTTP_400_BAD_REQUEST) for tile_type in tile_types:
res = client.get("/api/projects/{}/tasks/{}/{}/tiles.json".format(project.id, task.id, tile_type))
self.assertTrue(res.status_code == status.HTTP_400_BAD_REQUEST)
# Neither should an individual tile # Neither should an individual tile
# Z/X/Y coords are choosen based on node-odm test dataset for orthophoto_tiles/ # Z/X/Y coords are choosen based on node-odm test dataset for orthophoto_tiles/
res = client.get("/api/projects/{}/tasks/{}/tiles/16/16020/42443.png".format(project.id, task.id)) res = client.get("/api/projects/{}/tasks/{}/orthophoto/tiles/16/16020/42443.png".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND) self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND)
# Cannot access a tiles.json we have no access to # Cannot access a tiles.json we have no access to
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(other_project.id, other_task.id)) res = client.get("/api/projects/{}/tasks/{}/orthophoto/tiles.json".format(other_project.id, other_task.id))
self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND) self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND)
# Cannot access an individual tile we have no access to # Cannot access an individual tile we have no access to
res = client.get("/api/projects/{}/tasks/{}/tiles/16/16020/42443.png".format(other_project.id, other_task.id)) res = client.get("/api/projects/{}/tasks/{}/orthophoto/tiles/16/16020/42443.png".format(other_project.id, other_task.id))
self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND) self.assertTrue(res.status_code == status.HTTP_404_NOT_FOUND)
# Cannot download assets (they don't exist yet) # Cannot download assets (they don't exist yet)
@ -226,8 +228,9 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(res.status_code == status.HTTP_200_OK) self.assertTrue(res.status_code == status.HTTP_200_OK)
# Can access tiles.json # Can access tiles.json
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(project.id, task.id)) for tile_type in tile_types:
self.assertTrue(res.status_code == status.HTTP_200_OK) res = client.get("/api/projects/{}/tasks/{}/{}/tiles.json".format(project.id, task.id, tile_type))
self.assertTrue(res.status_code == status.HTTP_200_OK)
# Bounds are what we expect them to be # Bounds are what we expect them to be
# (4 coords in lat/lon) # (4 coords in lat/lon)
@ -236,8 +239,9 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(round(tiles['bounds'][0], 7) == -91.9945132) self.assertTrue(round(tiles['bounds'][0], 7) == -91.9945132)
# Can access individual tiles # Can access individual tiles
res = client.get("/api/projects/{}/tasks/{}/tiles/16/16020/42443.png".format(project.id, task.id)) for tile_type in tile_types:
self.assertTrue(res.status_code == status.HTTP_200_OK) res = client.get("/api/projects/{}/tasks/{}/{}/tiles/16/16020/42443.png".format(project.id, task.id, tile_type))
self.assertTrue(res.status_code == status.HTTP_200_OK)
# Restart a task # Restart a task
testWatch.clear() testWatch.clear()
@ -379,6 +383,20 @@ class TestApiTask(BootTransactionTestCase):
# orthophoto_extent should be none # orthophoto_extent should be none
self.assertTrue(task.orthophoto_extent is None) self.assertTrue(task.orthophoto_extent is None)
# but other extents should be populated
self.assertTrue(task.dsm_extent is not None)
self.assertTrue(task.dtm_extent is not None)
self.assertTrue(os.path.exists(task.assets_path("dsm_tiles")))
self.assertTrue(os.path.exists(task.assets_path("dtm_tiles")))
# Can access only tiles of available assets
res = client.get("/api/projects/{}/tasks/{}/dsm/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_200_OK)
res = client.get("/api/projects/{}/tasks/{}/dtm/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_200_OK)
res = client.get("/api/projects/{}/tasks/{}/orthophoto/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_400_BAD_REQUEST)
# Available assets should be missing orthophoto.tif type # Available assets should be missing orthophoto.tif type
# but others such as textured_model.zip should be available # but others such as textured_model.zip should be available
res = client.get("/api/projects/{}/tasks/{}/".format(project.id, task.id)) res = client.get("/api/projects/{}/tasks/{}/".format(project.id, task.id))

@ -1 +1 @@
Subproject commit bfc90c9cec21a06b88ed92f202a0a79901b8962d Subproject commit 3586a31f9071db5931b52c580bbf009f25e3b5ec

Wyświetl plik

@ -180,14 +180,26 @@ If a [Task](#task) has been canceled or has failed processing, or has completed
### Orthophoto TMS layer ### Orthophoto TMS layer
`GET /api/projects/{project_id}/tasks/{task_id}/tiles.json` `GET /api/projects/{project_id}/tasks/{task_id}/orthophoto/tiles.json`
`GET /api/projects/{project_id}/tasks/{task_id}/tiles/{Z}/{X}/{Y}.png` `GET /api/projects/{project_id}/tasks/{task_id}/orthophoto/tiles/{Z}/{X}/{Y}.png`
After a task has been successfully processed, a TMS layer is made available for inclusion in programs such as [Leaflet](http://leafletjs.com/) or [Cesium](http://cesiumjs.org). After a task has been successfully processed, a TMS layer is made available for inclusion in programs such as [Leaflet](http://leafletjs.com/) or [Cesium](http://cesiumjs.org).
<aside class="notice">If you use <a href="http://leafletjs.com/" target="_blank">Leaflet</a>, you'll need to pass the authentication token via querystring: /api/projects/{project_id}/tasks/{task_id}/tiles/{Z}/{X}/{Y}.png?jwt=your_token</aside> <aside class="notice">If you use <a href="http://leafletjs.com/" target="_blank">Leaflet</a>, you'll need to pass the authentication token via querystring: /api/projects/{project_id}/tasks/{task_id}/tiles/{Z}/{X}/{Y}.png?jwt=your_token</aside>
### Surface Model TMS layer
`GET /api/projects/{project_id}/tasks/{task_id}/dsm/tiles.json`
`GET /api/projects/{project_id}/tasks/{task_id}/dsm/tiles/{Z}/{X}/{Y}.png`
### Terrain Model TMS layer
`GET /api/projects/{project_id}/tasks/{task_id}/dtm/tiles.json`
`GET /api/projects/{project_id}/tasks/{task_id}/dtm/tiles/{Z}/{X}/{Y}.png`
### Pending Actions ### Pending Actions
In some circumstances, a [Task](#task) can have a pending action that requires some amount of time to be performed. In some circumstances, a [Task](#task) can have a pending action that requires some amount of time to be performed.