irie 0.0.35__py3-none-any.whl → 0.0.37__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of irie might be problematic. Click here for more details.
- irie/apps/context_processors.py +8 -0
- irie/apps/inventory/admin.py +4 -1
- irie/apps/inventory/forms.py +30 -1
- irie/apps/inventory/migrations/0004_datum_sensorgroup_sensor.py +43 -0
- irie/apps/inventory/migrations/0005_alter_sensor_group.py +19 -0
- irie/apps/inventory/migrations/0006_datum_asset_datum_locate_x_datum_locate_y_and_more.py +56 -0
- irie/apps/inventory/migrations/0007_sensor_name.py +19 -0
- irie/apps/inventory/migrations/0008_alter_sensor_dx_alter_sensor_dy_alter_sensor_dz_and_more.py +43 -0
- irie/apps/inventory/models.py +47 -17
- irie/apps/inventory/urls.py +4 -2
- irie/apps/inventory/views.py +55 -2
- irie/apps/templates/includes/sidebar.html +29 -10
- irie/apps/templates/inventory/asset-event-summary.html +17 -7
- irie/apps/templates/inventory/asset-profile.html +10 -2
- irie/apps/templates/inventory/asset-sensors.html +69 -0
- irie/apps/templates/inventory/sensor-upload.html +452 -0
- irie/apps/templates/layouts/base-fullscreen.html +3 -4
- irie/apps/templates/site/index.html +2 -2
- irie/core/settings.py +1 -0
- irie/init/data/nbi_definitions.json +1 -0
- irie/init/getNBIData.py +12 -0
- irie/init/management/commands/init_assets.py +11 -7
- irie/init/management/commands/init_cesmd.py +11 -8
- {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/METADATA +1 -1
- {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/RECORD +28 -20
- {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/WHEEL +1 -1
- {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/entry_points.txt +0 -0
- {irie-0.0.35.dist-info → irie-0.0.37.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
===----------------------------------------------------------------------===#
|
|
3
|
+
|
|
4
|
+
STAIRLab -- STructural Artificial Intelligence Laboratory
|
|
5
|
+
|
|
6
|
+
===----------------------------------------------------------------------===#
|
|
7
|
+
|
|
8
|
+
Chrystal Chern, Spring 2025
|
|
9
|
+
|
|
10
|
+
-->
|
|
11
|
+
{% extends "layouts/base.html" %}
|
|
12
|
+
|
|
13
|
+
{% block stylesheets %}
|
|
14
|
+
<style>
|
|
15
|
+
input[type="number"] {
|
|
16
|
+
-moz-appearance: textfield; /* Firefox */
|
|
17
|
+
appearance: textfield; /* Standard */
|
|
18
|
+
}
|
|
19
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
20
|
+
input[type="number"]::-webkit-outer-spin-button {
|
|
21
|
+
-webkit-appearance: none;
|
|
22
|
+
margin: 0;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
25
|
+
<script type="importmap">
|
|
26
|
+
{
|
|
27
|
+
"imports": {
|
|
28
|
+
"three": "https://cdn.jsdelivr.net/npm/three@0.172.0/build/three.module.js",
|
|
29
|
+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.172.0/examples/jsm/"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
{% endblock stylesheets %}
|
|
34
|
+
|
|
35
|
+
{% block title %} Add Sensor Group to {{ asset.calid }} {% endblock %}
|
|
36
|
+
|
|
37
|
+
{% block content %}
|
|
38
|
+
<h1>Add Sensor Group to <code>{{ asset.calid }}</code></h1>
|
|
39
|
+
|
|
40
|
+
<div class="py-4 align-right">
|
|
41
|
+
<a role="button" class="button btn btn-outline-primary"
|
|
42
|
+
href="{% url 'asset_profile' calid=asset.calid %}" class="me-1">Back to Structure</a>
|
|
43
|
+
<a role="button" class="button btn btn-outline-primary"
|
|
44
|
+
href="{% url 'asset_sensors' calid=asset.calid %}" class="me-1">Back to Sensors</a>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
<div class="container">
|
|
49
|
+
<div class="row align-right">
|
|
50
|
+
|
|
51
|
+
<!-- Left Side: Sensor Form -->
|
|
52
|
+
<div class="col-md-6" >
|
|
53
|
+
<form method="post">
|
|
54
|
+
{% csrf_token %}
|
|
55
|
+
|
|
56
|
+
<fieldset class="mb-4">
|
|
57
|
+
<legend>Sensor Group</legend>
|
|
58
|
+
{{ group_form.as_p }}
|
|
59
|
+
<div id="datum-info" class="mb-4 p-2 border rounded bg-light" style="display: none;">
|
|
60
|
+
<strong>Datum Location (x;y;z):</strong> <span id="datum-location">_, _, _</span> <br>
|
|
61
|
+
<strong>Datum Orientation (dx;dy;dz):</strong> <span id="datum-orientation">_, _, _</span>
|
|
62
|
+
</div>
|
|
63
|
+
</fieldset>
|
|
64
|
+
|
|
65
|
+
<fieldset>
|
|
66
|
+
<legend>Sensors</legend>
|
|
67
|
+
{{ formset.management_form }}
|
|
68
|
+
<div id="formset-container" class="container">
|
|
69
|
+
{% for form in formset %}
|
|
70
|
+
<div class="sensor-form mb-4">
|
|
71
|
+
<div class="row mb-3">
|
|
72
|
+
<div class="input-group">
|
|
73
|
+
<span class="input-group-text col-md-2 text-dark border-dark">{{ form.name.label }}</span>
|
|
74
|
+
<input id="{{form.name.html_name}}" type="text" name="{{form.name.html_name}}" class="form-control col-md-4 text-dark border-dark" required></input>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="row"><div class="col-md-12 d-flex align-items-center mb-3">
|
|
78
|
+
<div class="input-group">
|
|
79
|
+
<span class="input-group-text col-md-4 text-gray">{{ form.x.label }}</span>
|
|
80
|
+
<input id="{{form.x.html_name}}" type="number" step="any" name="{{form.x.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="input-group">
|
|
83
|
+
<span class="input-group-text col-md-4 text-gray">{{ form.y.label }}</span>
|
|
84
|
+
<input id="{{form.y.html_name}}" type="number" step="any" name="{{form.y.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="input-group">
|
|
87
|
+
<span class="input-group-text col-md-4 text-gray">{{ form.z.label }}</span>
|
|
88
|
+
<input id="{{form.z.html_name}}" type="number" step="any" name="{{form.z.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
89
|
+
</div>
|
|
90
|
+
</div></div>
|
|
91
|
+
<div class="row"><div class="col-md-12 d-flex align-items-center mb-3">
|
|
92
|
+
<div class="input-group">
|
|
93
|
+
<span class="input-group-text col-md-5 text-gray">{{ form.dx.label }}</span>
|
|
94
|
+
<input id="{{form.dx.html_name}}" type="number" step="any" name="{{form.dx.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="input-group">
|
|
97
|
+
<span class="input-group-text col-md-5 text-gray">{{ form.dy.label }}</span>
|
|
98
|
+
<input id="{{form.dy.html_name}}" type="number" step="any" name="{{form.dy.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="input-group">
|
|
101
|
+
<span class="input-group-text col-md-5 text-gray">{{ form.dz.label }}</span>
|
|
102
|
+
<input id="{{form.dz.html_name}}" type="number" step="any" name="{{form.dz.html_name}}" class="form-control col-md-8 text-gray" required></input>
|
|
103
|
+
</div>
|
|
104
|
+
</div></div>
|
|
105
|
+
|
|
106
|
+
<button class="remove-form btn btn-danger btn-sm ml-2">Remove</button>
|
|
107
|
+
</div>
|
|
108
|
+
{% endfor %}
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<button id="add-form" class="btn btn-sm button btn-outline-primary mb-3">Add Sensor</button>
|
|
112
|
+
</fieldset>
|
|
113
|
+
|
|
114
|
+
<button type="button" id="plot-btn" class="btn btn-info btn-sm mb-3">Plot</button>
|
|
115
|
+
|
|
116
|
+
<button type="submit" class="btn btn-sm button btn-success mb-3">Submit</button>
|
|
117
|
+
</form>
|
|
118
|
+
</div>
|
|
119
|
+
<!-- Right Side: Asset Geometry -->
|
|
120
|
+
<div class="col-md-6">
|
|
121
|
+
<h4>Geometry</h4>
|
|
122
|
+
{%if false %}
|
|
123
|
+
{% if asset.cesmd %}
|
|
124
|
+
<div id="sensors" class="card bg-white-100 border-1 rounded-0 shadow mb-3">
|
|
125
|
+
<div class="card-body text-center p-2">
|
|
126
|
+
<img src="{{ ASSETS_ROOT }}/inventory/ll{{ asset.cesmd|slice:"2:" }}.svg" class="img-fluid">
|
|
127
|
+
</div>
|
|
128
|
+
<div class="card-footer text-center">
|
|
129
|
+
<span class="small">
|
|
130
|
+
Source: <a rel="nofollow noreferrer" href="https://www.strongmotioncenter.org/cgi-bin/CESMD/stationhtml.pl?stationID={{asset.cesmd}}&network=CGS">CGS</a>
|
|
131
|
+
</span>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
{% endif %}
|
|
135
|
+
|
|
136
|
+
<div id="rendering" class="card bg-white-100 border-1 rounded-0 shadow">
|
|
137
|
+
<div class="card-body text-center">
|
|
138
|
+
<model-viewer
|
|
139
|
+
id="irie-viewer"
|
|
140
|
+
alt="3D Model"
|
|
141
|
+
{% if asset.rendering %}
|
|
142
|
+
src="{{ asset.rendering }}"
|
|
143
|
+
{% else %}
|
|
144
|
+
src="{{ ASSETS_ROOT }}/inventory/empty.glb"
|
|
145
|
+
{% endif %}
|
|
146
|
+
camera-controls
|
|
147
|
+
interaction-prompt="none"
|
|
148
|
+
camera-orbit="0deg 75deg 2m"
|
|
149
|
+
field-of-view="30deg"
|
|
150
|
+
style="width: 100%; height: 300px; background-color: #f0f0f0;">
|
|
151
|
+
</model-viewer>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="card-footer text-center">
|
|
154
|
+
Powered by <a href="https://veux.io/">veux</a>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
{% else %}
|
|
158
|
+
{% if asset.cesmd %}
|
|
159
|
+
<div id="sensors" class="card bg-white-100 border-1 rounded-0 shadow mb-3">
|
|
160
|
+
<div class="card-body text-center p-2">
|
|
161
|
+
<img src="{{ ASSETS_ROOT }}/inventory/ll{{ asset.cesmd|slice:"2:" }}.svg" class="img-fluid">
|
|
162
|
+
</div>
|
|
163
|
+
<div class="card-footer text-center">
|
|
164
|
+
<span class="small">
|
|
165
|
+
Source: <a rel="nofollow noreferrer" href="https://www.strongmotioncenter.org/cgi-bin/CESMD/stationhtml.pl?stationID={{asset.cesmd}}&network=CGS">CGS</a>
|
|
166
|
+
</span>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
{% endif %}
|
|
170
|
+
|
|
171
|
+
<div id="rendering" class="card bg-white-100 border-1 rounded-0 shadow">
|
|
172
|
+
<div class="card-body text-center" style="background-color: #f0f0f0;">
|
|
173
|
+
<!-- Our Three.js container -->
|
|
174
|
+
<div id="threejs-container"
|
|
175
|
+
style="width: 100%; height: 300px; background-color: #f0f0f0; margin: 0 auto;">
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="card-footer text-center">
|
|
179
|
+
Powered by <a href="https://veux.io/">veux</a>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
{% endif %}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
<!--
|
|
188
|
+
<div id="plot-container" style="display: none;">
|
|
189
|
+
<h3>3D Sensor Plot</h3>
|
|
190
|
+
<div id="sensor-plot"></div>
|
|
191
|
+
</div> -->
|
|
192
|
+
{%endblock content %}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
{% block javascripts %}
|
|
196
|
+
<script>
|
|
197
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
198
|
+
const formsetContainer = document.getElementById("formset-container");
|
|
199
|
+
const addButton = document.getElementById("add-form");
|
|
200
|
+
const totalForms = document.getElementById("id_form-TOTAL_FORMS");
|
|
201
|
+
|
|
202
|
+
function updateRemoveButtons() {
|
|
203
|
+
let forms = document.querySelectorAll(".sensor-form");
|
|
204
|
+
|
|
205
|
+
forms.forEach((form, index) => {
|
|
206
|
+
let removeButton = form.querySelector(".remove-form");
|
|
207
|
+
if (forms.length > 1)
|
|
208
|
+
removeButton.style.display = "inline-block";
|
|
209
|
+
else
|
|
210
|
+
removeButton.style.display = "none";
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
addButton.addEventListener("click", (e) => {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
let formNum = parseInt(totalForms.value);
|
|
217
|
+
let newForm = document.querySelector(".sensor-form").cloneNode(true);
|
|
218
|
+
|
|
219
|
+
// Update form attributes
|
|
220
|
+
newForm.innerHTML = newForm.innerHTML.replace(/form-(\d+)-/g, `form-${formNum}-`);
|
|
221
|
+
formsetContainer.appendChild(newForm);
|
|
222
|
+
|
|
223
|
+
// Increment the management form count
|
|
224
|
+
totalForms.value = formNum + 1;
|
|
225
|
+
|
|
226
|
+
updateRemoveButtons();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
formsetContainer.addEventListener("click", (e) => {
|
|
230
|
+
if (e.target.classList.contains("remove-form")) {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
if (document.querySelectorAll(".sensor-form").length > 1) {
|
|
233
|
+
e.target.closest(".sensor-form").remove();
|
|
234
|
+
totalForms.value = document.querySelectorAll(".sensor-form").length;
|
|
235
|
+
updateRemoveButtons();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Ensure the correct remove button state on load
|
|
241
|
+
updateRemoveButtons();
|
|
242
|
+
});
|
|
243
|
+
</script>
|
|
244
|
+
<script>
|
|
245
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
246
|
+
const datumSelect = document.getElementById("id_datum");
|
|
247
|
+
const datumInfoDiv = document.getElementById("datum-info");
|
|
248
|
+
const locationSpan = document.getElementById("datum-location");
|
|
249
|
+
const orientationSpan = document.getElementById("datum-orientation");
|
|
250
|
+
|
|
251
|
+
// Store datum data from Django
|
|
252
|
+
const datums = {{ datums|safe }};
|
|
253
|
+
|
|
254
|
+
datumSelect.addEventListener("change", function () {
|
|
255
|
+
const selectedDatumId = this.value;
|
|
256
|
+
const selectedDatum = datums.find(d => d.id == selectedDatumId);
|
|
257
|
+
|
|
258
|
+
if (selectedDatum) {
|
|
259
|
+
locationSpan.textContent = `${selectedDatum.locate_x}; ${selectedDatum.locate_y}; ${selectedDatum.locate_z}`;
|
|
260
|
+
orientationSpan.textContent = `${selectedDatum.orient_x}; ${selectedDatum.orient_y}; ${selectedDatum.orient_z}`;
|
|
261
|
+
datumInfoDiv.style.display = "block"; // Show info box
|
|
262
|
+
} else {
|
|
263
|
+
datumInfoDiv.style.display = "none"; // Hide if no datum selected
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
</script>
|
|
268
|
+
<script type="module">
|
|
269
|
+
|
|
270
|
+
import * as THREE from 'three';
|
|
271
|
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
272
|
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
273
|
+
|
|
274
|
+
function createArrow(origin, direction, length, color, headLength, headWidth, shaftRadius) {
|
|
275
|
+
const dir = direction.clone().normalize();
|
|
276
|
+
|
|
277
|
+
const shaftLength = length - headLength;
|
|
278
|
+
|
|
279
|
+
const arrowGroup = new THREE.Group();
|
|
280
|
+
|
|
281
|
+
const shaftGeometry = new THREE.CylinderGeometry(shaftRadius, shaftRadius, shaftLength, 16);
|
|
282
|
+
const shaftMaterial = new THREE.MeshStandardMaterial({ color: color });
|
|
283
|
+
const shaftMesh = new THREE.Mesh(shaftGeometry, shaftMaterial);
|
|
284
|
+
|
|
285
|
+
// CylinderGeometry is oriented along the Y-axis.
|
|
286
|
+
// Rotate it so it aligns with the group's local +Y (arrow "up").
|
|
287
|
+
// Then rotate the entire group to match `dir`.
|
|
288
|
+
shaftMesh.position.y = shaftLength / 2; // move it so its base starts at y=0
|
|
289
|
+
arrowGroup.add(shaftMesh);
|
|
290
|
+
|
|
291
|
+
const headGeometry = new THREE.ConeGeometry(shaftRadius * 2, headLength, 16);
|
|
292
|
+
const headMaterial = new THREE.MeshStandardMaterial({ color: color });
|
|
293
|
+
const headMesh = new THREE.Mesh(headGeometry, headMaterial);
|
|
294
|
+
|
|
295
|
+
headMesh.position.y = shaftLength + headLength / 2;
|
|
296
|
+
arrowGroup.add(headMesh);
|
|
297
|
+
|
|
298
|
+
arrowGroup.position.copy(origin);
|
|
299
|
+
|
|
300
|
+
// 5) Rotate the entire group so that +Y in local space points along `dir`
|
|
301
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
302
|
+
const quaternion = new THREE.Quaternion().setFromUnitVectors(up, dir);
|
|
303
|
+
arrowGroup.quaternion.copy(quaternion);
|
|
304
|
+
|
|
305
|
+
return arrowGroup;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
309
|
+
const container = document.getElementById('threejs-container');
|
|
310
|
+
// Fallback or real path
|
|
311
|
+
const modelPath = `{{ asset.rendering|default_if_none:"" }}`;
|
|
312
|
+
|
|
313
|
+
// 1) SETUP SCENE
|
|
314
|
+
const scene = new THREE.Scene();
|
|
315
|
+
scene.background = new THREE.Color(0xf0f0f0);
|
|
316
|
+
|
|
317
|
+
// 2) SETUP RENDERER
|
|
318
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
319
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
320
|
+
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
321
|
+
// renderer.outputEncoding = THREE.sRGBEncoding;
|
|
322
|
+
container.appendChild(renderer.domElement);
|
|
323
|
+
|
|
324
|
+
// 3) SETUP CAMERA
|
|
325
|
+
const camera = new THREE.PerspectiveCamera(
|
|
326
|
+
30, // fov
|
|
327
|
+
container.clientWidth / container.clientHeight, // aspect
|
|
328
|
+
0.1, // near
|
|
329
|
+
1000 // far
|
|
330
|
+
);
|
|
331
|
+
// Position: "0deg 75deg 2m" => we can interpret as an angle from horizontal
|
|
332
|
+
// Place the camera a bit above and away from the origin.
|
|
333
|
+
camera.position.set(0, 2, 2);
|
|
334
|
+
camera.lookAt(0, 0, 0);
|
|
335
|
+
|
|
336
|
+
// 4) ORBIT CONTROLS
|
|
337
|
+
const controls = new OrbitControls(camera, renderer.domElement);
|
|
338
|
+
controls.enableDamping = true;
|
|
339
|
+
controls.dampingFactor = 0.05;
|
|
340
|
+
controls.target.set(0, 0, 0);
|
|
341
|
+
|
|
342
|
+
// 5) LIGHTING (basic environment)
|
|
343
|
+
const ambientLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1.2);
|
|
344
|
+
scene.add(ambientLight);
|
|
345
|
+
|
|
346
|
+
const globalLight = new THREE.AmbientLight(0xffffff, 0.4);
|
|
347
|
+
scene.add(globalLight);
|
|
348
|
+
|
|
349
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
|
|
350
|
+
directionalLight.position.set(5, 10, 7.5);
|
|
351
|
+
scene.add(directionalLight);
|
|
352
|
+
|
|
353
|
+
// 6) LOAD THE GLB MODEL (if asset.rendering is not empty)
|
|
354
|
+
if (modelPath) {
|
|
355
|
+
const loader = new GLTFLoader();
|
|
356
|
+
loader.load(modelPath, (gltf) => {
|
|
357
|
+
const model = gltf.scene;
|
|
358
|
+
scene.add(model);
|
|
359
|
+
|
|
360
|
+
// Compute bounding box
|
|
361
|
+
const box = new THREE.Box3().setFromObject(model);
|
|
362
|
+
const size = box.getSize(new THREE.Vector3()).length();
|
|
363
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
364
|
+
|
|
365
|
+
// Adjust camera clipping
|
|
366
|
+
camera.near = size / 100;
|
|
367
|
+
camera.far = size * 100;
|
|
368
|
+
camera.updateProjectionMatrix();
|
|
369
|
+
|
|
370
|
+
// Move camera so the model is nicely framed
|
|
371
|
+
camera.position.copy(center);
|
|
372
|
+
// Move the camera out some distance (play with the multiplier)
|
|
373
|
+
camera.position.x += size;
|
|
374
|
+
camera.position.y += size;
|
|
375
|
+
camera.position.z += size;
|
|
376
|
+
camera.lookAt(center);
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
controls.target.copy(center);
|
|
380
|
+
controls.update();
|
|
381
|
+
},
|
|
382
|
+
undefined,
|
|
383
|
+
(error) => {
|
|
384
|
+
console.error('Error loading GLB:', error);
|
|
385
|
+
});
|
|
386
|
+
} else {
|
|
387
|
+
const axesHelper = new THREE.AxesHelper(1);
|
|
388
|
+
scene.add(axesHelper);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 7) HANDLE WINDOW RESIZE
|
|
392
|
+
window.addEventListener('resize', onWindowResize, false);
|
|
393
|
+
function onWindowResize() {
|
|
394
|
+
camera.aspect = container.clientWidth / container.clientHeight;
|
|
395
|
+
camera.updateProjectionMatrix();
|
|
396
|
+
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 8) ANIMATE LOOP
|
|
400
|
+
function animate() {
|
|
401
|
+
requestAnimationFrame(animate);
|
|
402
|
+
controls.update();
|
|
403
|
+
renderer.render(scene, camera);
|
|
404
|
+
}
|
|
405
|
+
animate();
|
|
406
|
+
|
|
407
|
+
const arrowObjects = [];
|
|
408
|
+
|
|
409
|
+
//
|
|
410
|
+
// Plot Button
|
|
411
|
+
//
|
|
412
|
+
const plotButton = document.getElementById('plot-btn');
|
|
413
|
+
plotButton.addEventListener('click', function () {
|
|
414
|
+
arrowObjects.forEach(arrow => {
|
|
415
|
+
scene.remove(arrow);
|
|
416
|
+
});
|
|
417
|
+
arrowObjects.length = 0;
|
|
418
|
+
|
|
419
|
+
// Grab sensor forms
|
|
420
|
+
const sensorForms = document.querySelectorAll('.sensor-form');
|
|
421
|
+
sensorForms.forEach((form) => {
|
|
422
|
+
const x = parseFloat(form.querySelector("input[name*='x']").value) || 0;
|
|
423
|
+
const y = parseFloat(form.querySelector("input[name*='y']").value) || 0;
|
|
424
|
+
const z = parseFloat(form.querySelector("input[name*='z']").value) || 0;
|
|
425
|
+
const dx = parseFloat(form.querySelector("input[name*='dx']").value) || 0;
|
|
426
|
+
const dy = parseFloat(form.querySelector("input[name*='dy']").value) || 0;
|
|
427
|
+
const dz = parseFloat(form.querySelector("input[name*='dz']").value) || 0;
|
|
428
|
+
|
|
429
|
+
// Create a direction vector from dx,dy,dz
|
|
430
|
+
const direction = new THREE.Vector3(dx, dy, dz);
|
|
431
|
+
const length = direction.length() || 0.1; // arrow length
|
|
432
|
+
direction.normalize();
|
|
433
|
+
|
|
434
|
+
const origin = new THREE.Vector3(x, y, z);
|
|
435
|
+
const color = 0xff0000;
|
|
436
|
+
const arrow = createArrow(
|
|
437
|
+
origin,
|
|
438
|
+
direction,
|
|
439
|
+
length,
|
|
440
|
+
color,
|
|
441
|
+
0.3*length, // head length
|
|
442
|
+
0.2*length, // head width
|
|
443
|
+
0.1*length // shaft radius
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
scene.add(arrow);
|
|
447
|
+
arrowObjects.push(arrow);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
</script>
|
|
452
|
+
{% endblock javascripts %}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
5
5
|
<title>{% block title %}{% endblock %} | BRACE2</title>
|
|
6
6
|
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
8
8
|
<meta name="title" content="BRACE2 - Sign up page">
|
|
9
9
|
<meta name="author" content="PEER">
|
|
10
10
|
<meta name="description" content="BRACE2 is a platform for structural health monitoring.">
|
|
11
|
+
<link rel="canonical" href="https://structures.live{{ request.path }}">
|
|
11
12
|
|
|
12
13
|
<!-- Open Graph / Facebook -->
|
|
13
14
|
<meta property="og:type" content="website">
|
|
@@ -44,12 +45,10 @@
|
|
|
44
45
|
|
|
45
46
|
</head>
|
|
46
47
|
<body>
|
|
47
|
-
|
|
48
48
|
{% block content %}{% endblock content %}
|
|
49
49
|
|
|
50
50
|
{% include 'includes/scripts.html' %}
|
|
51
51
|
|
|
52
|
-
{% block javascripts %}{% endblock javascripts %}
|
|
53
|
-
|
|
52
|
+
{% block javascripts %}{% endblock javascripts %}
|
|
54
53
|
</body>
|
|
55
54
|
</html>
|
|
@@ -219,7 +219,7 @@ td.left-text{vertical-align:middle}
|
|
|
219
219
|
<div class="col-lg-5 order-lg-2 mb-5 mb-lg-0">
|
|
220
220
|
<h2 class="h1">Digital Twins</h2>
|
|
221
221
|
<p class="mb-4">The <em>IRiE</em> engine is used to realize high-fidelity digital twins that are backed by
|
|
222
|
-
state-of-the-art analysis platforms like <a href="https://pypi.org/project/
|
|
222
|
+
state-of-the-art analysis platforms like <a href="https://pypi.org/project/sees">OpenSees</a> for nonlinear finite element analysis and <em><a href="https://chrystalchern.github.io/mdof">mdof</a></em> for system identification.</p>
|
|
223
223
|
</div>
|
|
224
224
|
<div class="col-lg-4 order-lg-1 card shadow"><img src="{{ ASSETS_ROOT }}/img/twin.png"
|
|
225
225
|
alt="Digital twins"></div>
|
|
@@ -258,7 +258,7 @@ td.left-text{vertical-align:middle}
|
|
|
258
258
|
<a href="https://stairlab.berkeley.edu/software/opensees/">
|
|
259
259
|
<img class="first-column-layout"
|
|
260
260
|
src="{{ ASSETS_ROOT }}/content_images/brace/opensees.jpg" alt="opensees"></a></td>
|
|
261
|
-
<td class=full-center-text><a href="https://pypi.org/project/
|
|
261
|
+
<td class=full-center-text><a href="https://pypi.org/project/sees" >sees</a></td>
|
|
262
262
|
<td class=left-text>Direct and idiomatic bindings to <a href="https://github.com/claudioperez/OpenSeesRT" >
|
|
263
263
|
<samp>OpenSeesRT</samp></a> kernel for finite element analysis.</td>
|
|
264
264
|
</tr>
|
irie/core/settings.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"1": "State Name", "2": "Highway Agency District", "3": "County Name", "4": "Place Code", "5A": "Record Type", "5B": "Route Signing Prefix Code", "5C": "Designated Level of Service Code", "5D": "Route Number", "5E": "Directional Suffix Code", "6A": "Features Intersected", "7": "Facility Carried By Structure", "8": "Structure Number", "9": "Location", "10": "Inventory Route - Minimum Vertical Clearance", "11": "Mile Point", "12": "Base Highway Network", "13A": "LRS Inventory Route", "13B": "Subroute Number", "16": "Latitude", "17": "Longitude", "19": "Bypass or Detour Length", "20": "Toll Status", "21": "Maintenance Responsibility", "22": "Owner Agency", "26": "Functional Class Of Inventory Route", "27": "Year Built", "28A": "Lanes On the Structure", "28B": "Lanes Under the Structure", "29": "Average Daily Traffic", "30": "Year of Average Daily Traffic", "31": "Design Load Descriptor", "32": "Approach Roadway Width", "33": "Bridge Median Code", "34": "Skew Angle", "35": "Structure Flared", "36A": "Bridge Railings", "36B": "Transitions", "36C": "Approach Guardrail", "36D": "Bridge Guardrail Ends", "37": "Historical Significance Code", "38": "Navigation Control Code", "39": "Navigation Vertical Clearance", "40": "Navigation Horizontal Clearance", "41": "Structure Operational Status Code", "42A": "Type of Service on Bridge Code", "42B": "Type Of Service Under Bridge Code", "43A": "Main Span Material", "43B": "Main Span Design", "44A": "Approach Spans Material", "44B": "Approach Spans Design", "45": "Number of Spans in Main Unit", "46": "Number of Approach Spans", "47": "Inventory Route Total Horizontal Clearance", "48": "Length of Maximum Span", "49": "Structure Length", "50A": "Left Curb/Sidewalk Width", "50B": "Right Curb/Sidewalk Width", "51": "Bridge Roadway Width Curb to Curb", "52": "Deck Width - Out to Out", "53": "Minimum Vertical Clearance Over Bridge Roadway", "54A": "Minimum Vertical Underclearance Reference Feature", "54B": "Minimum Vertical Underclearance", "55A": "Minimum Lateral Underclearance Reference Feature", "55B": "Minimum Lateral Underclearance on Right", "56": "Minimum Lateral Underclearance on Left", "58": "Deck Condition Rating", "59": "Superstructure Condition Rating", "60": "Substructure Condition Rating", "61": "Channel and Channel Protection Condition Rating", "62": "Culverts Condition Rating", "63": "Operating Rating Method Code", "64": "Operating Rating", "65": "Inventory Rating Method Code", "66": "Inventory Rating", "67": "Structural Evaluation Appraisal", "68": "Deck Geometry Appraisal", "69": "Underclearance Appraisal Vertical and Horizontal", "70": "Bridge Posting Code", "71": "Waterway Adequacy Appraisal", "72": "Approach Alignment Appraisal", "75A": "Type of Work Proposed", "75B": "Work Done By", "76": "Length of Structure Improvement", "90": "Inspection Date", "91": "Designated Inspection Frequency", "92A": "Fracture Critical Details", "92B": "Underwater Inspection", "92C": "Other Special Inspection", "93A": "Fracture Critical Detail Date", "93B": "Underwater Inspection Date", "93C": "Other Special Inspection Date", "94": "Bridge Improvement Cost", "95": "Roadway Improvement Cost", "96": "Total Project Cost", "97": "Year of Improvement Cost Estimate", "98A": "Neighboring State Name", "98B": "Neighboring State Percent Responsibility", "99": "Border Bridge Structure Number", "100": "STRAHNET Highway Designation", "101": "Parallel Structure Designation Code", "102": "Direction of Traffic Code", "103": "Temporary Structure Designation Code", "104": "Inventory Route NHS Code", "105": "Federal Lands Highways Code", "106": "Year Reconstructed", "107": "Deck Structure Type Code", "108A": "Wearing Surface Type Code", "108B": "Membrane Type Code", "108C": "Deck Protection Code", "109": "Average Daily Truck Traffic (Percent ADT)", "110": "Designated National Truck Network Code", "111": "Pier Abutment Protection Code", "112": "NBIS Minimum Bridge Length", "113": "Scour Critical Bridge Value", "114": "Future Average Daily Traffic", "115": "Year of Future Average Daily Traffic", "116": "Minimum Vertical Clearance - Lift Bridge", "Computed": "Average Daily Truck Traffic (Volume)", "CAT29": "Deck Area", "CAT10": "Bridge Condition", "CAT23": "Condition Code", "null": "City (InfoBridge Place Code-Name)"}
|
irie/init/getNBIData.py
CHANGED
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
#
|
|
13
13
|
# Claudio M. Perez
|
|
14
14
|
#
|
|
15
|
+
# TODO:
|
|
16
|
+
#
|
|
17
|
+
# - Add option for "SELECTED_TAB": "NBETab",
|
|
18
|
+
#
|
|
19
|
+
# - Perhaps add something like:
|
|
20
|
+
# --filter-calid calids.txt
|
|
21
|
+
# This will be useful for testing, eg, (chrystal's first version)
|
|
22
|
+
# python getNBIData.py yearly.json --filter-calid <(echo "33 0214L")
|
|
23
|
+
#
|
|
24
|
+
# or
|
|
25
|
+
# python getNBIData.py | python getNBIData.py /dev/stdin <(echo "33 0214L")
|
|
26
|
+
#
|
|
15
27
|
import sys
|
|
16
28
|
import json
|
|
17
29
|
import requests
|
|
@@ -98,10 +98,14 @@ def load_assets(NBI_DATA):
|
|
|
98
98
|
|
|
99
99
|
# 1. Collect routes of interest
|
|
100
100
|
ROUTES = set()
|
|
101
|
-
for
|
|
102
|
-
if "calid" not in bridge:
|
|
101
|
+
for calid in CESMD: #BRIDGES.values():
|
|
102
|
+
# if "calid" not in bridge:
|
|
103
|
+
try:
|
|
104
|
+
calid = Asset.objects.get(calid=calid).calid
|
|
105
|
+
except:
|
|
103
106
|
continue
|
|
104
|
-
|
|
107
|
+
|
|
108
|
+
calid = calid.split(" ")[0].replace("-", " ")
|
|
105
109
|
nbi = get_nbi(calid, missing_ok=True)
|
|
106
110
|
if nbi is not None:
|
|
107
111
|
ROUTES.add(get_route(nbi))
|
|
@@ -112,7 +116,7 @@ def load_assets(NBI_DATA):
|
|
|
112
116
|
for item in NBI_DATA:
|
|
113
117
|
calid = item.replace(" ", "-")
|
|
114
118
|
nbi = get_nbi(item)
|
|
115
|
-
config = find_bridge(BRIDGES, calid)
|
|
119
|
+
config = None # find_bridge(BRIDGES, calid)
|
|
116
120
|
try:
|
|
117
121
|
if skip(nbi, ROUTES) or item == "33 0726L":
|
|
118
122
|
continue
|
|
@@ -143,9 +147,9 @@ def load_assets(NBI_DATA):
|
|
|
143
147
|
|
|
144
148
|
count += 1
|
|
145
149
|
|
|
146
|
-
if config:
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
# if config:
|
|
151
|
+
# asset.bridge_sensors = json.dumps(config["accelerometers"]["bridge_channels"])
|
|
152
|
+
# asset.ground_sensors = json.dumps(config["accelerometers"]["ground_channels"])
|
|
149
153
|
|
|
150
154
|
asset.save()
|
|
151
155
|
|
|
@@ -13,18 +13,21 @@ with open(DATA/"cgs_data.json") as f:
|
|
|
13
13
|
|
|
14
14
|
class Command(BaseCommand):
|
|
15
15
|
def handle(self, *args, **kwargs):
|
|
16
|
-
with open(DATA/"cgs-stations.json") as f:
|
|
17
|
-
|
|
16
|
+
# with open(DATA/"cgs-stations.json") as f:
|
|
17
|
+
# stations = json.load(f)
|
|
18
18
|
|
|
19
19
|
count = 0
|
|
20
20
|
try:
|
|
21
|
-
for
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
for calid, (cesmd, route, name) in CESMD.items():
|
|
22
|
+
try:
|
|
23
|
+
asset = Asset.objects.get(calid=calid)
|
|
24
|
+
except Asset.DoesNotExist:
|
|
25
|
+
asset = Asset()
|
|
26
|
+
asset.is_complete = False
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
asset.cesmd =
|
|
27
|
-
asset.
|
|
28
|
+
asset.name = name
|
|
29
|
+
asset.cesmd = cesmd
|
|
30
|
+
asset.calid = calid
|
|
28
31
|
asset.cgs_data = CGS_DATA.get(cesmd, {})
|
|
29
32
|
asset.save()
|
|
30
33
|
print(asset)
|