Skip to content

Commit e7d59d1

Browse files
Merge pull request #36 from developmentseed/update-items-page
Update items page to match collections page
2 parents c283839 + 74b4a8c commit e7d59d1

File tree

2 files changed

+107
-100
lines changed

2 files changed

+107
-100
lines changed

runtimes/eoapi/stac/eoapi/stac/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ async def item_collection(
529529
item_collection,
530530
template_name="items",
531531
title=f"{collection_id} items",
532+
limit=limit,
532533
)
533534

534535
elif output_type == MimeTypes.csv:
Lines changed: 106 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% include "header.html" %}
1+
{% extends "base.html" %}
22

33
{% set show_prev_link = false %}
44
{% set show_next_link = false %}
@@ -8,125 +8,131 @@
88
{% set urlq = url + '?' %}
99
{% endif %}
1010

11-
11+
{% block content %}
1212
<nav aria-label="breadcrumb">
1313
<ol class="breadcrumb bg-light">
1414
{% for crumb in crumbs %}
1515
{% if not loop.last %}
16-
<li class="breadcrumb-item"><a href="{{ crumb.url }}/">{{ crumb.part }}</a></li>
17-
{% else %}<li class="breadcrumb-item active" aria-current="page">{{ crumb.part }}</li>
16+
<li class="breadcrumb-item"><a href="{{ crumb.url }}/">{{ crumb.part }}</a></li>
17+
{% else %}
18+
<li class="breadcrumb-item active" aria-current="page">{{ crumb.part }}</li>
1819
{% endif %}
1920
{% endfor %}
2021

2122
<li class="ml-auto json-link"><a target="_blank" href="{{ urlq }}f=geojson">GeoJSON</a></li>
2223
</ol>
2324
</nav>
2425

25-
<h1>Collection Items: {{ response.title or response.id }}</h1>
26-
27-
<div id="map" class="rounded" style="width:100%; height:400px;">Loading...</div>
28-
29-
<p>
30-
<b>Number of matching items:</b> {{ response.numberMatched }}<br/>
31-
<b>Number of returned items:</b> {{ response.numberReturned }}<br/>
32-
</p>
33-
34-
<div class="form-row" style="margin-bottom:10px;" id="controls">
35-
{% for link in response.links %}
36-
{% if link.rel == 'previous' %}
37-
<div class="col-auto"><a class="btn btn-secondary" title="previous page" href="{{ link.href }}">prev</a></div>
38-
{% endif %}
39-
{% endfor %}
40-
<div class="col-auto">
41-
<select class="form-control" id="limit"> <!-- TODO: dynamically populate the values based on oga_max_limit -->
42-
<option value="10">page size</option>
43-
<option>10</option>
44-
<option>100</option>
45-
<option>1000</option>
46-
<option>10000</option>
47-
</select>
26+
<h1 class="my-4">
27+
<span class="d-block text-uppercase text-muted h6 mb-0">Collection Items:</span>
28+
{{ response.title or response.id }}
29+
</h1>
30+
{% if response.features|length > 0 %}
31+
<div class="d-flex flex-row align-items-center mb-4">
32+
<div class="flex-grow-1">
33+
Showing {{ response.numberReturned }} of {{ response.numberMatched }} items
4834
</div>
49-
{% for link in response.links %}
50-
{% if link.rel == 'next' %}
51-
<div class="col-auto"><a class="btn btn-secondary" title="next page" href="{{ link.href }}">next</a></div>
35+
<div class="form-inline ml-auto" style="gap: 10px">
36+
<div class="d-flex">
37+
<label for="limit">Page size: </label>
38+
<select class="form-control form-control-sm ml-1" id="limit" aria-label="Select page size"> <!-- TODO: dynamically populate the values based on oga_max_limit -->
39+
<option value="10" {% if limit == 10 %}selected{% endif %}>10</option>
40+
<option value="25" {% if limit == 25 %}selected{% endif %}>25</option>
41+
<option value="50" {% if limit == 50 %}selected{% endif %}>50</option>
42+
<option value="100" {% if limit == 100 %}selected{% endif %}>100</option>
43+
</select>
44+
</div>
45+
{% if response.links|length > 0 %}
46+
<div class="btn-group btn-group-sm" role="group" aria-label="Paginate">
47+
{% for link in response.links %}
48+
{% if link.rel == 'prev' or link.rel == 'previous' %}
49+
<a class="btn btn-secondary" title="previous page" href="{{ link.href }}">« prev</a>
50+
{% endif %}
51+
{% endfor %}
52+
{% for link in response.links %}
53+
{% if link.rel == 'next' %}
54+
<a class="btn btn-secondary" title="next page" href="{{ link.href }}">next »</a>
55+
{% endif %}
56+
{% endfor %}
5257
{% endif %}
53-
{% endfor %}
58+
</div>
59+
</div>
5460
</div>
61+
62+
<div id="map" class="rounded mb-2" style="width:100%; height:400px;">Loading...</div>
63+
5564
<div class="table-responsive">
56-
{% if response.features is defined and response.features|length > 0 %}
57-
<table class="table">
58-
<thead class="thead-light">
59-
<th>ID</th>
60-
{% for key, value in response.features.0.properties.items() %}
61-
<th style="font-size: 13px">{{ key }}</th>
62-
{% endfor %}
63-
</thead>
64-
<tbody>
65-
{% for feature in response.features %}
66-
<tr style="font-size: 11px">
67-
<td><a target="_blank" href="{{ template.api_root }}/collections/{{ feature.collection }}/items/{{ feature.id }}">{{ feature.id }}</a></td>
68-
{% for key, value in feature.properties.items() %}
69-
<td style="overflow: hidden; text-overflow: ellipsis; max-width: 200px; white-space: nowrap;">{{ value }}</td>
70-
{% endfor %}
71-
</tr>
72-
{% endfor %}
73-
</tbody>
74-
</table>
75-
{% endif %}
65+
{% if response.features and response.features|length > 0 %}
66+
<table class="table">
67+
<thead class="thead-light">
68+
<th>ID</th>
69+
{% for key, value in response.features.0.properties.items() %}
70+
<th style="font-size: 13px">{{ key }}</th>
71+
{% endfor %}
72+
</thead>
73+
<tbody>
74+
{% for feature in response.features %}
75+
<tr style="font-size: 11px">
76+
<td><a target="_blank" href="{{ template.api_root }}/collections/{{ feature.collection }}/items/{{ feature.id }}">{{ feature.id }}</a></td>
77+
{% for key, value in feature.properties.items() %}
78+
<td style="overflow: hidden; text-overflow: ellipsis; max-width: 200px; white-space: nowrap;">{{ value }}</td>
79+
{% endfor %}
80+
</tr>
81+
{% endfor %}
82+
</tbody>
83+
</table>
84+
{% endif %}
7685
</div>
86+
{% else %}
87+
<div class="text-center mx-auto py-5 w-50">
88+
<p class="h4 mb-3">No items found</p>
89+
<p>You need to add STAC Collections and Items; for example by following the <a href="https://github.com/vincentsarago/MAXAR_opendata_to_pgstac">MAXAR open data demo</a> or <a href="https://github.com/developmentseed/eoAPI/tree/main/demo">other demos.</a></p>
90+
</div>
91+
{% endif %}
7792

7893
<script>
79-
80-
81-
82-
function changePageSize() {
83-
var url = "{{ template.api_root }}/collections/{{ response.features[0].collection }}/items?";
84-
const searchParams = new URLSearchParams(window.location.search);
85-
searchParams.set('limit', $("#limit").val());
86-
url += searchParams.toString();
87-
window.location.href = url;
88-
}
89-
$(function() {
90-
//
91-
// mapping
92-
//
93-
var geojson = {{ response|tojson }};
94-
95-
var features = (geojson.features) ? geojson.features : [];
96-
var hasGeom = features.some(feat => feat.geometry);
97-
if (hasGeom) {
98-
var map = L.map('map').setView([0, 0], 1);
99-
map.addLayer(new L.TileLayer(
100-
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
101-
maxZoom: 18,
102-
attribution: 'Map data &copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
94+
window.addEventListener("load", function () {
95+
// Pagination
96+
document.getElementById("limit").addEventListener("change", (event) => {
97+
// Set new page size
98+
const limit = event.target.value;
99+
let url = "{{ template.api_root }}/collections/{{ response.id }}/items?";
100+
const searchParams = new URLSearchParams(window.location.search);
101+
searchParams.set('limit', limit);
102+
url += searchParams.toString();
103+
window.location.href = url;
104+
});
105+
106+
// Mapping
107+
const geojson = {{ response|tojson }};
108+
109+
const hasGeom = geojson.features && geojson.features.some(feat => feat.geometry);
110+
if (hasGeom) {
111+
const map = L.map('map').setView([0, 0], 1);
112+
map.addLayer(new L.TileLayer(
113+
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
114+
maxZoom: 18,
115+
attribution: 'Map data &copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
116+
}
117+
));
118+
119+
function addPopup(feature, layer) {
120+
const aElm = document.createElement('a');
121+
aElm.setAttribute('href', `{{ template.api_root }}/collections/${feature.collection}/items/${feature.id}`);
122+
aElm.setAttribute('target', '_blank');
123+
aElm.innerText = feature.id;
124+
layer.bindPopup(aElm);
103125
}
104-
));
105126

106-
function addPopup(feature, layer) {
107-
var aElm = document.createElement('a');
108-
aElm.setAttribute('href', `{{ template.api_root }}/collections/${feature.collection}/items/${feature.id}`);
109-
aElm.setAttribute('target', '_blank');
110-
aElm.innerText = feature.id;
111-
layer.bindPopup(aElm);
112-
}
113-
114-
var features = L.geoJSON(geojson, {
115-
onEachFeature: addPopup
116-
}).addTo(map);
117-
118-
map.fitBounds(features.getBounds());
119-
} else {
120-
document.getElementById("map").style.display = "none";
121-
}
127+
const features = L.geoJSON(geojson, {
128+
onEachFeature: addPopup,
129+
weight: 2
130+
}).addTo(map);
122131

123-
//
124-
// event handling
125-
//
126-
$("#limit").on("change", function() {
127-
changePageSize();
132+
map.fitBounds(features.getBounds());
133+
} else {
134+
document.getElementById("map").style.display = "none";
135+
}
128136
});
129-
});
130137
</script>
131-
132-
{% include "footer.html" %}
138+
{% endblock %}

0 commit comments

Comments
 (0)