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){
// Go through the list of tiles and return
// only those that match a particular type
// Go through the list of map items and return
// only those that match a particular type (in tile format)
const tiles = [];
this.props.mapItems.forEach(mapItem => {
@ -64,7 +64,7 @@ class MapView extends React.Component {
render(){
const { opacity } = this.state;
const mapTypeButtons = [
let mapTypeButtons = [
{
label: "Orthophoto",
type: "orthophoto"
@ -77,7 +77,10 @@ class MapView extends React.Component {
label: "Terrain Model",
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">
<div className="map-type-selector btn-group" role="group">

Wyświetl plik

@ -45,18 +45,31 @@ class Map extends React.Component {
this.imageryLayers = [];
this.basemaps = {};
this.mapBounds = null;
this.autolayers = null;
this.loadImageryLayers = this.loadImageryLayers.bind(this);
}
loadImageryLayers(){
loadImageryLayers(forceAddLayers = false){
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
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 = [];
// Request new tiles
return new Promise((resolve, reject) => {
this.tileJsonRequests = [];
@ -74,12 +87,16 @@ class Map extends React.Component {
maxZoom: info.maxzoom,
tms: info.scheme === 'tms',
opacity: this.props.opacity / 100
}).addTo(this.map);
});
// Associate metadata with this layer
meta.name = info.name;
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
const task = {
id: meta.task,
@ -118,6 +135,9 @@ class Map extends React.Component {
mapBounds.extend(bounds);
this.mapBounds = mapBounds;
// Add layer to layers control
this.autolayers.addOverlay(layer, info.name);
done();
})
.fail((_, __, err) => done(err))
@ -138,10 +158,17 @@ class Map extends React.Component {
this.map = Leaflet.map(this.container, {
scrollWheelZoom: true,
measureControl: true,
positionControl: true
});
const measureControl = Leaflet.control.measure({
primaryLengthUnit: 'meters',
secondaryLengthUnit: 'feet',
primaryAreaUnit: 'sqmeters',
secondaryAreaUnit: 'acres'
});
measureControl.addTo(this.map);
if (showBackground) {
this.basemaps = {
"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();
Leaflet.control.scale({
@ -173,22 +206,9 @@ class Map extends React.Component {
}).addTo(this.map);
this.map.attributionControl.setPrefix("");
this.loadImageryLayers().then(() => {
this.loadImageryLayers(true).then(() => {
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 => {
// Find first tile layer at the selected coordinates
for (let layer of this.imageryLayers){
@ -199,7 +219,6 @@ class Map extends React.Component {
}
});
});
}
componentDidUpdate(prevProps) {
@ -208,7 +227,9 @@ class Map extends React.Component {
});
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)
# tiles.json should not be accessible at this point
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_400_BAD_REQUEST)
tile_types = ['orthophoto', 'dsm', 'dtm']
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
# 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)
# 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)
# 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)
# Cannot download assets (they don't exist yet)
@ -226,8 +228,9 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(res.status_code == status.HTTP_200_OK)
# Can access tiles.json
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_200_OK)
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_200_OK)
# Bounds are what we expect them to be
# (4 coords in lat/lon)
@ -236,8 +239,9 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(round(tiles['bounds'][0], 7) == -91.9945132)
# Can access individual tiles
res = client.get("/api/projects/{}/tasks/{}/tiles/16/16020/42443.png".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_200_OK)
for tile_type in tile_types:
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
testWatch.clear()
@ -379,6 +383,20 @@ class TestApiTask(BootTransactionTestCase):
# orthophoto_extent should be 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
# but others such as textured_model.zip should be available
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
`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).
<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
In some circumstances, a [Task](#task) can have a pending action that requires some amount of time to be performed.