@mapcatch/util 1.0.15 → 2.0.0

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.
Files changed (81) hide show
  1. package/dist/catchUtil.min.esm.js +67984 -14011
  2. package/dist/catchUtil.min.js +2695 -55
  3. package/package.json +22 -3
  4. package/src/constants/annotation_color.js +7 -0
  5. package/src/constants/annotation_draw_style.js +228 -0
  6. package/src/constants/annotation_label_style.js +76 -0
  7. package/src/constants/annotation_style.js +118 -0
  8. package/src/constants/cameras.js +1 -1
  9. package/src/constants/crs.js +31473 -31473
  10. package/src/constants/error_codes.js +44 -0
  11. package/src/constants/height_colors.js +1 -0
  12. package/src/constants/index.js +9 -2
  13. package/src/constants/map_style.js +11 -0
  14. package/src/constants/measurement_fields.js +3 -3
  15. package/src/{event.js → event/event.js} +1 -14
  16. package/src/event/event_bus.js +5 -0
  17. package/src/event/index.js +2 -0
  18. package/src/gl-operations/constants.js +9 -11
  19. package/src/gl-operations/default_options.js +5 -5
  20. package/src/gl-operations/index.js +166 -239
  21. package/src/gl-operations/reglCommands/contours.js +20 -20
  22. package/src/gl-operations/reglCommands/default.js +34 -34
  23. package/src/gl-operations/reglCommands/hillshading.js +116 -116
  24. package/src/gl-operations/reglCommands/index.js +6 -6
  25. package/src/gl-operations/reglCommands/multiLayers.js +55 -55
  26. package/src/gl-operations/reglCommands/transitions.js +24 -24
  27. package/src/gl-operations/reglCommands/util.js +54 -54
  28. package/src/gl-operations/renderer.js +69 -69
  29. package/src/gl-operations/shaders/transform.js +2 -2
  30. package/src/gl-operations/shaders/util/rgbaToFloat.glsl +11 -11
  31. package/src/gl-operations/texture_manager.js +58 -58
  32. package/src/gl-operations/util.js +154 -154
  33. package/src/index.js +14 -2
  34. package/src/measure/index.js +198 -0
  35. package/src/measure/tile_cache.js +88 -0
  36. package/src/mvs/index.js +26 -0
  37. package/src/mvs/protos/index.js +12 -0
  38. package/src/mvs/protos/proto_10.js +155 -0
  39. package/src/observation_pretict.js +168 -0
  40. package/src/photo-parser/exif/gps_tags.js +33 -0
  41. package/src/photo-parser/exif/ifd1_tags.js +22 -0
  42. package/src/photo-parser/exif/index.js +130 -0
  43. package/src/photo-parser/exif/parse_image.js +290 -0
  44. package/src/photo-parser/exif/string_values.js +137 -0
  45. package/src/photo-parser/exif/tags.js +75 -0
  46. package/src/photo-parser/exif/tiff_tags.js +35 -0
  47. package/src/photo-parser/exif/util.js +103 -0
  48. package/src/photo-parser/image-size/detector.js +24 -0
  49. package/src/photo-parser/image-size/fromFile.js +55 -0
  50. package/src/photo-parser/image-size/index.js +2 -0
  51. package/src/photo-parser/image-size/lookup.js +37 -0
  52. package/src/photo-parser/image-size/types/bmp.js +10 -0
  53. package/src/photo-parser/image-size/types/cur.js +16 -0
  54. package/src/photo-parser/image-size/types/dds.js +10 -0
  55. package/src/photo-parser/image-size/types/gif.js +11 -0
  56. package/src/photo-parser/image-size/types/heif.js +35 -0
  57. package/src/photo-parser/image-size/types/icns.js +112 -0
  58. package/src/photo-parser/image-size/types/ico.js +74 -0
  59. package/src/photo-parser/image-size/types/index.js +43 -0
  60. package/src/photo-parser/image-size/types/j2c.js +11 -0
  61. package/src/photo-parser/image-size/types/jp2.js +22 -0
  62. package/src/photo-parser/image-size/types/jpg.js +157 -0
  63. package/src/photo-parser/image-size/types/ktx.js +18 -0
  64. package/src/photo-parser/image-size/types/png.js +36 -0
  65. package/src/photo-parser/image-size/types/pnm.js +74 -0
  66. package/src/photo-parser/image-size/types/psd.js +10 -0
  67. package/src/photo-parser/image-size/types/svg.js +100 -0
  68. package/src/photo-parser/image-size/types/tga.js +14 -0
  69. package/src/photo-parser/image-size/types/tiff.js +92 -0
  70. package/src/photo-parser/image-size/types/utils.js +83 -0
  71. package/src/photo-parser/image-size/types/webp.js +67 -0
  72. package/src/photo-parser/index.js +181 -0
  73. package/src/report/annotations_report.js +446 -0
  74. package/src/report/index.js +2 -0
  75. package/src/report/map_util.js +81 -0
  76. package/src/report/pdf_creator.js +247 -0
  77. package/src/report/report.js +583 -0
  78. package/src/transform.js +204 -0
  79. package/src/util.js +371 -75
  80. package/CHANGELOG.md +0 -60
  81. /package/src/constants/{colors.js → dsm_colors.js} +0 -0
@@ -0,0 +1,583 @@
1
+ import moment from 'moment'
2
+ import { formatDate, getAverageValue, getAppVersion } from '../util'
3
+ import _ from 'lodash'
4
+ import {getFeatures} from './map_util'
5
+
6
+ export default async function generateReport (params) {
7
+ let {projectInfo, taskInfo, reportInfo, photosString, tilejson} = params
8
+ let photos = JSON.parse(photosString)
9
+ let report = {
10
+ title: '成果质量报告',
11
+ projectName: projectInfo.name,
12
+ taskName: taskInfo.name,
13
+ project_id: taskInfo.project_id,
14
+ task_id: taskInfo.task_id,
15
+ user_id: taskInfo.user_id,
16
+ data_type: taskInfo.data_type,
17
+ sections: [
18
+ {
19
+ title: '任务概览',
20
+ content: [
21
+ {
22
+ type: 'form',
23
+ items: getTaskPreview(taskInfo, photos)
24
+ },
25
+ {
26
+ type: 'sub-title',
27
+ label: '耗时统计'
28
+ },
29
+ {
30
+ type: 'tree',
31
+ items: getTimeStatistic(reportInfo)
32
+ },
33
+ {
34
+ type: 'image',
35
+ imageType: 'overview'
36
+ },
37
+ {
38
+ type: 'page-line' // 分页
39
+ }
40
+ ]
41
+ },
42
+ {
43
+ title: '空三质量报告',
44
+ content: [
45
+ {
46
+ type: 'sub-title',
47
+ label: '测区概况及覆盖度'
48
+ },
49
+ {
50
+ type: 'form',
51
+ items: getSurveyOverview(photos, reportInfo)
52
+ },
53
+ {
54
+ type: 'image',
55
+ imageType: 'overlap'
56
+ },
57
+ {
58
+ type: 'page-line' // 分页
59
+ },
60
+ {
61
+ type: 'sub-title',
62
+ label: '相机校正'
63
+ },
64
+ {
65
+ type: 'form',
66
+ items: getCameraOverview(photos, reportInfo)
67
+ },
68
+ {
69
+ type: 'cameras',
70
+ items: getCameras(reportInfo)
71
+ },
72
+ {
73
+ type: 'page-line' // 分页
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ title: '重建质量报告',
79
+ content: [
80
+ {
81
+ type: 'sub-title',
82
+ label: '运行环境'
83
+ },
84
+ {
85
+ type: 'form',
86
+ items: getReconstructEnvironment(reportInfo, taskInfo)
87
+ },
88
+ {
89
+ type: 'sub-title',
90
+ label: '重建参数'
91
+ },
92
+ {
93
+ type: 'form',
94
+ items: getReconstructParams(reportInfo)
95
+ }
96
+ ]
97
+ }
98
+ ]
99
+ }
100
+ // 建了二维地图才有以下报告
101
+ if (tilejson) {
102
+ report.sections[1].content.push(
103
+ {
104
+ type: 'sub-title',
105
+ label: '图像位置'
106
+ },
107
+ {
108
+ type: 'image',
109
+ imageType: 'photos',
110
+ errorFeatures: await getErrorFeatures(photos, reportInfo, tilejson),
111
+ errorData: getErrorData(reportInfo.image_POS_diff.filter(d => !!d.pos_diff))
112
+ },
113
+ {
114
+ type: 'page-line' // 分页
115
+ }
116
+ )
117
+ report.sections[2].content.push(
118
+ {
119
+ type: 'sub-title',
120
+ label: '数字表面模型'
121
+ },
122
+ {
123
+ type: 'image',
124
+ imageType: 'dsm'
125
+ }
126
+ )
127
+ }
128
+ if (reportInfo.gcp_quality?.length) {
129
+ report.sections[1].content.push({
130
+ type: 'sub-title',
131
+ label: '像控点精度'
132
+ },{
133
+ type: 'third-title',
134
+ label: '控制点'
135
+ },{
136
+ type: 'gcps',
137
+ data: getControlPoints(reportInfo.gcp_quality, 'control point')
138
+ },{
139
+ type: 'third-title',
140
+ label: '检查点'
141
+ },{
142
+ type: 'gcps',
143
+ data: getControlPoints(reportInfo.gcp_quality, 'check point')
144
+ },
145
+ {
146
+ type: 'page-line' // 分页
147
+ })
148
+ }
149
+ return report
150
+ }
151
+
152
+ function getTaskPreview (taskInfo, photos) {
153
+ return [
154
+ {
155
+ label: '任务名称',
156
+ value: taskInfo.name
157
+ },
158
+ {
159
+ label: '任务类型',
160
+ value: getDataType(taskInfo.data_type)
161
+ },
162
+ {
163
+ label: '数据采集时间',
164
+ value: formatDate(taskInfo.captured_at)
165
+ },
166
+ {
167
+ label: '照片数量',
168
+ value: photos.length
169
+ },
170
+ {
171
+ label: '开始时间',
172
+ value: formatDate(taskInfo.started_at)
173
+ },
174
+ {
175
+ label: '完成时间',
176
+ value: formatDate(taskInfo.completed_at)
177
+ }
178
+ ]
179
+ }
180
+
181
+ function getTimeStatistic (reportInfo) {
182
+ return [
183
+ {
184
+ level: 1,
185
+ label: '总耗时',
186
+ value: formatDuration(((+reportInfo.at_time || 0) + (+reportInfo.reconstruction_time || 0)) * 60),
187
+ children: [
188
+ {
189
+ level: 2,
190
+ label: '空三时间',
191
+ value: formatDuration(reportInfo.at_time * 60),
192
+ children: [
193
+ {
194
+ level: 3,
195
+ label: '特征提取时间',
196
+ value: formatDuration(reportInfo.feature_extraction_time * 60)
197
+ },
198
+ {
199
+ level: 3,
200
+ label: 'sfm时间',
201
+ value: formatDuration(reportInfo.sfm_time * 60)
202
+ }
203
+ ]
204
+ },
205
+ {
206
+ level: 2,
207
+ label: '重建时间',
208
+ value: formatDuration(reportInfo.reconstruction_time * 60)
209
+ }
210
+ ]
211
+ }
212
+ ]
213
+ }
214
+
215
+ function formatDuration (seconds) { // 单位为秒, 格式化为可读的时间
216
+ if (!seconds) {
217
+ return '暂无'
218
+ }
219
+ let duration = moment.duration(seconds, 'seconds')
220
+ if (duration.days() > 0) {
221
+ return `${duration.days()}天${duration.hours()}小时${duration.minutes()}分${duration.seconds()}秒`
222
+ } else if (duration.hours() > 0) {
223
+ return `${duration.hours()}小时${duration.minutes()}分钟${duration.seconds()}秒`
224
+ } else if (duration.minutes() > 0) {
225
+ return `${duration.minutes()}分钟${duration.seconds()}秒`
226
+ } else {
227
+ return `${duration.seconds()}秒`
228
+ }
229
+ }
230
+
231
+ function getDataType (type) {
232
+ if (type === 'infrared') {
233
+ return '热红外'
234
+ } else if (type === 'multispectral') {
235
+ return '多光谱'
236
+ } else {
237
+ return '可见光'
238
+ }
239
+ }
240
+
241
+ function getSurveyOverview (photos, reportInfo) {
242
+ return [
243
+ {
244
+ label: '照片数量',
245
+ value: photos.length
246
+ },
247
+ {
248
+ label: '飞行高度',
249
+ value: reportInfo.flight_height ? `${_.round(reportInfo.flight_height, 2)}米` : '暂无'
250
+ },
251
+ {
252
+ label: '连接点数',
253
+ value: reportInfo.tie_point_count
254
+ },
255
+ {
256
+ label: '地面分辨率',
257
+ value: reportInfo.scene_gsd ? `${_.round(reportInfo.scene_gsd, 2)}米` : '暂无'
258
+ },
259
+ {
260
+ label: '测区面积',
261
+ value: reportInfo.scene_area ? `${_.round(reportInfo.scene_area, 2)}平方米` : '暂无'
262
+ },
263
+ {
264
+ label: '重投影误差',
265
+ value: reportInfo.residual_rmse ? `${_.round(reportInfo.residual_rmse, 2)}pixel` : '暂无'
266
+ }
267
+ ]
268
+ }
269
+
270
+ function getCameraOverview (photos, reportInfo) {
271
+ return [
272
+ {
273
+ label: '相机数量',
274
+ value: reportInfo.input_camera_count || '暂无'
275
+ },
276
+ {
277
+ label: '照片数量',
278
+ value: photos.length
279
+ },
280
+ {
281
+ label: '入网照片',
282
+ value: reportInfo.input_image_count - reportInfo.removed_image_count
283
+ },
284
+ {
285
+ label: '未入网照片',
286
+ value: reportInfo.removed_image_count
287
+ },
288
+ {
289
+ label: '入网率',
290
+ value: `${_.round(100 * (reportInfo.input_image_count - reportInfo.removed_image_count) / reportInfo.input_image_count, 2)}%`
291
+ }
292
+ ]
293
+ }
294
+
295
+ function getControlPoints (gcps, type) {
296
+ let points = gcps.filter(d => d.usage === type)
297
+ let cols = [
298
+ {
299
+ title: 'ID',
300
+ key: 'id',
301
+ width: 80
302
+ },
303
+ {
304
+ title: '刺点数',
305
+ key: 'observation_count'
306
+ },
307
+ {
308
+ title: '重投影误差(px)',
309
+ key: 'residual',
310
+ width: 150
311
+ },
312
+ {
313
+ title: 'X误差(cm)',
314
+ key: 'error_x'
315
+ },
316
+ {
317
+ title: 'Y误差(cm)',
318
+ key: 'error_y'
319
+ },
320
+ {
321
+ title: 'Z误差(cm)',
322
+ key: 'error_z'
323
+ },
324
+ {
325
+ title: '总计',
326
+ key: 'error_total'
327
+ }
328
+ ]
329
+ let data = [], x_norm = 0, y_norm = 0, z_norm = 0, count = 0
330
+ if (!points.length) {
331
+ return {cols, data}
332
+ }
333
+ points.forEach(p => {
334
+ let {id, observation_count, error, observation_residual} = p
335
+ data.push({
336
+ id,
337
+ observation_count,
338
+ residual: observation_residual ? _.round(getAverageValue(observation_residual, 'residual'), 5) : undefined,
339
+ error_x: _.round(error[0] * 100, 5),
340
+ error_y: _.round(error[1] * 100, 5),
341
+ error_z: _.round(error[2] * 100, 5),
342
+ error_total: _.round(Math.sqrt(error[0] * error[0] + error[1] * error[1] + error[2] * error[2]) * 100, 5)
343
+ })
344
+ x_norm += error[0] * error[0]
345
+ y_norm += error[1] * error[1]
346
+ z_norm += error[2] * error[2]
347
+ count += observation_count
348
+ })
349
+ data.push({
350
+ id: '总计',
351
+ observation_count: count,
352
+ error_x: _.round(Math.sqrt(x_norm / points.length) * 100, 5),
353
+ error_y: _.round(Math.sqrt(y_norm / points.length) * 100, 5),
354
+ error_z: _.round(Math.sqrt(z_norm / points.length) * 100, 5),
355
+ error_total: _.round(Math.sqrt((x_norm + y_norm + z_norm) / points.length) * 100, 5)
356
+ })
357
+ return {cols, data}
358
+ }
359
+
360
+ function getCameras (reportInfo) {
361
+ let {AT_camera_parameters, initial_camera_parameters} = reportInfo
362
+ let cameras = []
363
+ let calibrationCols = [
364
+ {
365
+ title: '',
366
+ key: 'name',
367
+ width: 80
368
+ },
369
+ {
370
+ title: '焦距[px]',
371
+ key: 'focalLength'
372
+ },
373
+ {
374
+ title: '主点X[px]',
375
+ key: 'ppX'
376
+ },
377
+ {
378
+ title: '主点Y[px]',
379
+ key: 'ppY'
380
+ },
381
+ {
382
+ title: 'K1',
383
+ key: 'k1'
384
+ },
385
+ {
386
+ title: 'K2',
387
+ key: 'k2'
388
+ },
389
+ {
390
+ title: 'K3',
391
+ key: 'k3'
392
+ },
393
+ {
394
+ title: 'P1',
395
+ key: 'p1'
396
+ },
397
+ {
398
+ title: 'P2',
399
+ key: 'p2'
400
+ }
401
+ ]
402
+ AT_camera_parameters.forEach((item) => {
403
+ cameras.push({
404
+ cameraId: item.id,
405
+ forms: [
406
+ {
407
+ label: '相机名称',
408
+ value: item.camera_name || '暂无'
409
+ },
410
+ {
411
+ label: '成像模式',
412
+ value: '暂无'
413
+ },
414
+ {
415
+ label: '传感器尺寸',
416
+ value: '暂无'
417
+ },
418
+ {
419
+ label: '像幅大小',
420
+ value: `${item.width} × ${item.height}`
421
+ }
422
+ ],
423
+ calibrationCols,
424
+ calibrationData: getCameraCalibrationData(item.id, AT_camera_parameters, initial_camera_parameters, calibrationCols)
425
+ })
426
+ })
427
+ return cameras
428
+ }
429
+
430
+ function getCameraCalibrationData (camera_id, AT_camera_parameters, initial_camera_parameters, columns) {
431
+ let data = ['校正前', '校正后', '差值'].map(d => {
432
+ return {name: d}
433
+ })
434
+ let initialCamera = initial_camera_parameters.find(d => d.id === camera_id)
435
+ let atCamera = AT_camera_parameters.find(d => d.id === camera_id)
436
+ if (!initialCamera || !atCamera) {
437
+ return data
438
+ }
439
+ for(let i = 0;i < columns.length - 1;i++) {
440
+ let precition = i > 5 ? 6 : 4
441
+ data[0][columns[i + 1].key] = _.round(initialCamera.parameters[i], precition)
442
+ data[1][columns[i + 1].key] = _.round(atCamera.parameters[i], precition)
443
+ data[2][columns[i + 1].key] = _.round(atCamera.parameters[i] - initialCamera.parameters[i], precition)
444
+ }
445
+ return data
446
+ }
447
+
448
+ // 照片位置的中误差
449
+ function getErrorData (diffs) {
450
+ let quadraticSumX = 0,quadraticSumY = 0,quadraticSumZ = 0
451
+ let n = diffs.length
452
+ diffs.forEach(d => {
453
+ quadraticSumX += Math.pow(d.pos_diff[0], 2)
454
+ quadraticSumY += Math.pow(d.pos_diff[1], 2)
455
+ quadraticSumZ += Math.pow(d.pos_diff[2], 2)
456
+ })
457
+ let err = {
458
+ ex: _.round(Math.sqrt(quadraticSumX / n) * 100, 5),
459
+ ey: _.round(Math.sqrt(quadraticSumY / n) * 100, 5),
460
+ ez: _.round(Math.sqrt(quadraticSumZ / n) * 100, 5),
461
+ exy: _.round(Math.sqrt((quadraticSumX + quadraticSumY) / n) * 100, 5),
462
+ et: _.round(Math.sqrt((quadraticSumX + quadraticSumY + quadraticSumZ) / n) * 100, 5)
463
+ }
464
+ let unit = 'cm'
465
+ if (Object.keys(err).find(k => err[k] > 100)) {
466
+ unit = 'm'
467
+ for(let key in err) {
468
+ err[key] = _.round(err[key] / 100, 5)
469
+ }
470
+ }
471
+ return {
472
+ data: [err],
473
+ cols: [
474
+ {
475
+ title: `X中误差(${unit})`,
476
+ key: 'ex'
477
+ },
478
+ {
479
+ title: `Y中误差(${unit})`,
480
+ key: 'ey'
481
+ },
482
+ {
483
+ title: `Z中误差(${unit})`,
484
+ key: 'ez'
485
+ },
486
+ {
487
+ title: `XY中误差(${unit})`,
488
+ key: 'exy'
489
+ },
490
+ {
491
+ title: `总中误差(${unit})`,
492
+ key: 'et'
493
+ }
494
+ ]
495
+ }
496
+ }
497
+
498
+ async function getErrorFeatures (photos, reportInfo, tilejson) {
499
+ let {image_POS_diff} = reportInfo
500
+ let {bounds} = tilejson
501
+ let geoDiff = Math.max(bounds[2] - bounds[0], bounds[3] - bounds[1])
502
+ let zoom = Math.log2(690 * 360 / (geoDiff * 512))
503
+ return Object.assign({bounds}, getFeatures(photos, image_POS_diff.filter(d => !!d.pos_diff), zoom))
504
+ }
505
+
506
+ function getReconstructEnvironment (reportInfo, taskInfo) {
507
+ return [
508
+ {
509
+ label: 'CPU型号',
510
+ value: reportInfo.cpu_name || '暂无'
511
+ },
512
+ {
513
+ label: 'GPU型号',
514
+ value: reportInfo.gpu_name || '暂无'
515
+ },
516
+ {
517
+ label: '内存',
518
+ value: reportInfo.max_ram ? `${_.round(reportInfo.max_ram, 2)}GB` : '暂无'
519
+ },
520
+ {
521
+ label: 'SDK版本(空三)',
522
+ value: reportInfo.sdk_version_AT || '暂无'
523
+ },
524
+ {
525
+ label: 'SDK版本(重建)',
526
+ value: reportInfo.sdk_version_3D || '暂无'
527
+ },
528
+ {
529
+ label: '软件版本',
530
+ value: getAppVersion(taskInfo)
531
+ }
532
+ ]
533
+ }
534
+
535
+ function getReconstructParams (reportInfo) {
536
+ return [
537
+ {
538
+ label: '重建精度',
539
+ value: getReconstructAccurency(reportInfo.reconstruction_level)
540
+ },
541
+ {
542
+ label: '分块数量',
543
+ value: reportInfo.block_count || '暂无'
544
+ },
545
+ {
546
+ label: '输出成果类型',
547
+ value: reportInfo.production_type || '暂无'
548
+ },
549
+ {
550
+ label: '2D成果坐标系',
551
+ value: getCoordinateSystem(reportInfo.production_cs_2d)
552
+ },
553
+ {
554
+ label: '3D成果坐标系',
555
+ value: getCoordinateSystem(reportInfo.production_cs_3d)
556
+ }
557
+ ]
558
+ }
559
+
560
+ function getReconstructAccurency (accurency) {
561
+ accurency = Number(accurency)
562
+ if (accurency === 1) {
563
+ return '超高精度'
564
+ } else if (accurency === 2) {
565
+ return '高精度'
566
+ } else if (accurency === 3) {
567
+ return '中精度'
568
+ } else {
569
+ return '暂无'
570
+ }
571
+ }
572
+
573
+ function getCoordinateSystem (cs) {
574
+ if (!cs) {
575
+ return '暂无'
576
+ }
577
+ let enums = ['LOCAL_ENU', 'LOCAL', 'Geographic', 'Projected', 'Geocentric', 'Unsupported']
578
+ if (cs.epsg_code) {
579
+ return `EPSG:${cs.epsg_code}`
580
+ } else {
581
+ return enums[cs.type]
582
+ }
583
+ }