@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,198 @@
1
+ import cdt2d from 'cdt2d'
2
+ import _ from 'lodash'
3
+ import TileCache from './tile_cache'
4
+ import { getTileCoord, getTilePixelCoord, geographicToCartesian } from '../transform'
5
+ import { rgbaToFloat } from '../util'
6
+
7
+ class Measurement {
8
+ constructor (options) {
9
+ if (!options.loadTileImage) {
10
+ throw new Error('未指定加载瓦片数据的方法')
11
+ }
12
+ this.tileCache = new TileCache({
13
+ loadTileImage: options.loadTileImage,
14
+ maxCacheSize: options.maxCacheSize || 200
15
+ })
16
+ }
17
+
18
+
19
+ // 从dsm中获取指定位置的高程
20
+ async getFloatValue (lngLat, options) {
21
+ if (lngLat[2]) {
22
+ // 如果传入的坐标本身带有高程,则不用去dsm中获取高程
23
+ return _.round(lngLat[2], 4)
24
+ }
25
+ let {tilejson} = options
26
+ if (!tilejson) {
27
+ // 没有dsm
28
+ return 0
29
+ }
30
+ let { max_zoom } = tilejson
31
+ let { min, max } = tilejson.metadata
32
+ let tileCoord = getTileCoord(max_zoom, lngLat)
33
+ let pixelCoord = getTilePixelCoord(max_zoom, lngLat)
34
+ let tile = this.tileCache.getTile(tileCoord, options)
35
+ if (!tile) {
36
+ tile = this.tileCache.addTile(tileCoord, options)
37
+ }
38
+ if (!tile.loaded) {
39
+ await tile.tileData
40
+ }
41
+ if (!tile.tileData) {
42
+ // 没找到瓦片,说明该点超出瓦片范围,默认给一个平均值
43
+ return _.round((min + max) / 2, 4)
44
+ }
45
+ let pixelIndex = (pixelCoord.y * 512 + pixelCoord.x) * 4
46
+ let value = rgbaToFloat([
47
+ tile.tileData[pixelIndex],
48
+ tile.tileData[pixelIndex + 1],
49
+ tile.tileData[pixelIndex + 2],
50
+ tile.tileData[pixelIndex + 3]
51
+ ])
52
+ if (Math.abs(value + 99999) <= Math.max(Math.abs(value), Math.abs(-99999)) * 0.0001) {
53
+ // 无效值
54
+ return _.round((min + max) / 2, 4)
55
+ }
56
+ return _.round(value, 4)
57
+ }
58
+
59
+ async getMeasureInfo (options) {
60
+ let { data_type, geometry } = options
61
+ let type = geometry.type
62
+ let properties = {}
63
+ if (type === 'Point') {
64
+ // 点要素
65
+ let value = await this.getFloatValue(geometry.coordinates, options)
66
+ if (data_type === 'infrared') {
67
+ // 红外
68
+ properties.temprature = _.round(value, 4)
69
+ properties.lng = geometry.coordinates[0]
70
+ properties.lat = geometry.coordinates[1]
71
+ } else {
72
+ geometry.coordinates[2] = value
73
+ properties.height = value
74
+ properties.lng = geometry.coordinates[0]
75
+ properties.lat = geometry.coordinates[1]
76
+ }
77
+ } else if (type === 'LineString') {
78
+ let coordinates = geometry.coordinates
79
+ let slopes = []
80
+ let values = await Promise.all(coordinates.map(coord => this.getFloatValue(coord, options)))
81
+ if (data_type === 'infrared') {
82
+ // 红外
83
+ properties.distance2d = this.getDistance2D(coordinates)
84
+ properties.temprature_min = _.round(_.min(values), 4)
85
+ properties.temprature_max = _.round(_.max(values), 4)
86
+ } else {
87
+ coordinates.forEach((coord, index) => {
88
+ coord[2] = values[index]
89
+ if (index > 0) {
90
+ slopes.push(this.getSlope(coordinates[index - 1], coordinates[index]))
91
+ }
92
+ })
93
+ properties.distance2d = this.getDistance2D(coordinates)
94
+ properties.distance3d = this.getDistance3D(coordinates)
95
+ properties.height_min = _.min(values)
96
+ properties.height_max = _.max(values)
97
+ properties.slope_min = _.min(slopes)
98
+ properties.slope_max = _.max(slopes)
99
+ }
100
+ } else if (type === 'Polygon') {
101
+ let coordinates = geometry.coordinates[0]
102
+ let values = await Promise.all(coordinates.map(coord => this.getFloatValue(coord, options)))
103
+ if (data_type === 'infrared') {
104
+ // 红外
105
+ properties.distance2d = this.getDistance2D(coordinates)
106
+ properties.area2d = this.getArea(coordinates).area2d
107
+ properties.temprature_min = _.round(_.min(values), 4)
108
+ properties.temprature_max = _.round(_.max(values), 4)
109
+ } else {
110
+ coordinates.forEach((coord, index) => {
111
+ coord[2] = values[index]
112
+ })
113
+ properties.distance2d = this.getDistance2D(coordinates)
114
+ properties.distance3d = this.getDistance3D(coordinates)
115
+ properties.height_min = _.min(values)
116
+ properties.height_max = _.max(values)
117
+ let area = this.getArea(coordinates)
118
+ properties.area2d = area.area2d
119
+ properties.area3d = area.area3d
120
+ }
121
+ }
122
+ return { geometry, properties }
123
+ }
124
+
125
+ getSlope (from, to) {
126
+ let d = this.getDistance3D([from, to])
127
+ let hDiff = Math.abs(from[2] - to[2]) // 高差
128
+ return _.round((Math.asin(hDiff / d) * 180) / Math.PI, 4)
129
+ }
130
+
131
+ getDistance2D (coordinates) {
132
+ let d = 0
133
+ for (let i = 1; i < coordinates.length; i++) {
134
+ let p1 = geographicToCartesian([coordinates[i - 1][0], coordinates[i - 1][1], 0])
135
+ let p2 = geographicToCartesian([coordinates[i][0], coordinates[i][1], 0])
136
+ d += Math.sqrt(
137
+ Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2) + Math.pow(p1[2] - p2[2], 2)
138
+ )
139
+ }
140
+ return _.round(d, 4)
141
+ }
142
+
143
+ getDistance3D (coordinates) {
144
+ let d = 0
145
+ for (let i = 1; i < coordinates.length; i++) {
146
+ let p1 = geographicToCartesian(coordinates[i - 1])
147
+ let p2 = geographicToCartesian(coordinates[i])
148
+ d += Math.sqrt(
149
+ Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2) + Math.pow(p1[2] - p2[2], 2)
150
+ )
151
+ }
152
+ return _.round(d, 4)
153
+ }
154
+
155
+ getArea (coordinates) {
156
+ let triangles = []
157
+ let flatCoordinates
158
+ let projs = [coordinates.map(d => [d[0], d[1]]), coordinates.map(d => [d[0], d[2]]), coordinates.map(d => [d[1], d[2]])] // 三个投影平面
159
+ let flatCoordinates2d = projs[0] // 2D地图的投影平面
160
+
161
+ while (!triangles.length && projs.length) { // 寻找可成功分解的投影平面
162
+ flatCoordinates = projs.shift()
163
+ flatCoordinates.pop() // 去掉最后一个重复的点
164
+ let edges = []
165
+ flatCoordinates.forEach((coord, index) => {
166
+ if (index === flatCoordinates.length - 1) {
167
+ edges.push([index, 0])
168
+ } else {
169
+ edges.push([index, index + 1])
170
+ }
171
+ })
172
+ triangles = cdt2d(flatCoordinates, edges, { exterior: false })
173
+ }
174
+
175
+ let area2d = 0,
176
+ area3d = 0
177
+ triangles.forEach(triangle => {
178
+ let [i1, i2, i3] = triangle
179
+ area2d += this.getTriangleArea(
180
+ [flatCoordinates2d[i1][0], flatCoordinates2d[i1][1], 0],
181
+ [flatCoordinates2d[i2][0], flatCoordinates2d[i2][1], 0],
182
+ [flatCoordinates2d[i3][0], flatCoordinates2d[i3][1], 0]
183
+ )
184
+ area3d += this.getTriangleArea(coordinates[i1], coordinates[i2], coordinates[i3])
185
+ })
186
+ return { area2d: _.round(area2d, 4), area3d: _.round(area3d, 4) }
187
+ }
188
+
189
+ // 海伦公式:空间中三角形三个顶点坐标(经纬度),计算三角形面积
190
+ getTriangleArea (p1, p2, p3) {
191
+ let a = this.getDistance3D([p1, p2])
192
+ let b = this.getDistance3D([p2, p3])
193
+ let c = this.getDistance3D([p3, p1])
194
+ return Math.sqrt((a + b + c) * (a + b - c) * (a + c - b) * (b + c - a)) / 4
195
+ }
196
+ }
197
+
198
+ export default Measurement
@@ -0,0 +1,88 @@
1
+ import { decode, toRGBA8 } from 'upng-js'
2
+ import _ from 'lodash'
3
+
4
+ class TileCache {
5
+ constructor (options) {
6
+ this.tiles = []
7
+ this._loadTileImage = options.loadTileImage
8
+ this.MAX_SIZE = options.maxCacheSize
9
+ }
10
+
11
+ addTile (tileCoord, options) {
12
+ let { user_id, project_id, task_id, data_type } = options
13
+ let tile = {
14
+ loaded: false,
15
+ user_id,
16
+ project_id,
17
+ task_id,
18
+ data_type,
19
+ tileCoord
20
+ }
21
+ tile.tileData = this._loadTileData(tile, options)
22
+ this.tiles.push(tile)
23
+ return tile
24
+ }
25
+
26
+ async _loadTileData (tile) {
27
+ let { user_id, project_id, task_id, data_type, tileCoord } = tile
28
+ let originalTile = null
29
+ try {
30
+ originalTile = await this._loadTileImage(user_id, project_id, task_id, data_type, tileCoord)
31
+ } catch (err) {
32
+ console.error(err)
33
+ originalTile = null
34
+ }
35
+ if (!originalTile) {
36
+ tile.loaded = true
37
+ tile.lastVisited = new Date()
38
+ tile.tileData = null
39
+ return tile
40
+ }
41
+ const img = decode(originalTile)
42
+ const rgba = new Uint8Array(toRGBA8(img)[0])
43
+ tile.loaded = true
44
+ tile.lastVisited = new Date()
45
+ tile.tileData = rgba
46
+
47
+ if (this.tiles.length > this.MAX_SIZE) {
48
+ this.removeTile()
49
+ }
50
+ return tile
51
+ }
52
+
53
+ getTile (tileCoord, options) {
54
+ let { user_id, task_id, data_type } = options
55
+ for (let tile of this.tiles) {
56
+ if (
57
+ tile.user_id === user_id &&
58
+ tile.task_id === task_id &&
59
+ tile.data_type === data_type &&
60
+ _.isEqual(tile.tileCoord, tileCoord)
61
+ ) {
62
+ tile.lastVisited = new Date()
63
+ return tile
64
+ }
65
+ }
66
+ return null
67
+ }
68
+
69
+ removeTile () {
70
+ // 删除离上次访问时间最长的那个瓦片
71
+ let tileIndex = -1,
72
+ tile = null
73
+ for (let i = 0; i < this.tiles.length; i++) {
74
+ if (tile && tile.lastVisited > this.tiles[i].lastVisited) {
75
+ continue
76
+ }
77
+ tileIndex = i
78
+ tile = this.tiles[i]
79
+ }
80
+ this.tiles.splice(tileIndex, 1)
81
+ }
82
+
83
+ clear () {
84
+ this.tiles = []
85
+ }
86
+ }
87
+
88
+ export default TileCache
@@ -0,0 +1,26 @@
1
+ import Pbf from 'pbf'
2
+ import getProtoStr from './protos'
3
+ import {compile} from 'pbf/compile'
4
+ import schema from 'protocol-buffers-schema'
5
+
6
+ function _getProtoVersion (taskInfo) {
7
+ let version = '1.0'
8
+ if (taskInfo.metadata?.environment) {
9
+ return taskInfo.metadata.environment.protoVersion
10
+ }
11
+ return version
12
+ }
13
+
14
+ export function parseMVS (buffer, taskInfo) {
15
+ if (!taskInfo.task_id || !buffer) {
16
+ return
17
+ }
18
+ let protoVersion = _getProtoVersion(taskInfo)
19
+ let protoStr = getProtoStr(protoVersion)
20
+ if (!protoStr) {
21
+ throw new Error('不支持的格式')
22
+ }
23
+ let proto = schema.parse(protoStr)
24
+ let {MVSBlock} = compile(proto)
25
+ return MVSBlock.read(new Pbf(buffer))
26
+ }
@@ -0,0 +1,12 @@
1
+ import proto_10 from './proto_10'
2
+
3
+ function getProtoStr (version) {
4
+ switch (version) {
5
+ case '1.0':
6
+ return proto_10
7
+ default:
8
+ return ''
9
+ }
10
+ }
11
+
12
+ export default getProtoStr
@@ -0,0 +1,155 @@
1
+ export default `
2
+ syntax = "proto2";
3
+ package mipmap.engine.message;
4
+
5
+ message Point3d {
6
+ required double x = 1;
7
+ required double y = 2;
8
+ required double z = 3;
9
+ }
10
+
11
+ message Point2f {
12
+ required float x = 1;
13
+ required float y = 2;
14
+ }
15
+
16
+ message Point3f {
17
+ required float x = 1;
18
+ required float y = 2;
19
+ required float z = 3;
20
+ }
21
+
22
+ message ImageRect {
23
+ required int32 x = 1;
24
+ required int32 y = 2;
25
+ required int32 width = 3;
26
+ required int32 height = 4;
27
+ }
28
+
29
+ message BoundingBox3d {
30
+ repeated Point3d corner = 1; //长度为8,按照Eigen::BoundingBox3d排列
31
+ }
32
+
33
+ message CoordinateSystem {
34
+ enum CoordinateSystemType {
35
+ LOCAL_ENU = 0;
36
+ LOCAL = 1;
37
+ Geographic = 2;
38
+ Projected = 3;
39
+ Geocentric = 4;
40
+ Unsupported = 5;
41
+ };
42
+ required CoordinateSystemType type = 1;
43
+ optional int32 epsg_code = 2;
44
+ optional string wkt = 3;
45
+ repeated double offset = 4;
46
+ repeated double origin_point = 5;
47
+ }
48
+
49
+ message ImageMetaData {
50
+ optional Point3d position = 2;
51
+ optional CoordinateSystem position_cs = 201;
52
+ optional Point3d position_sigma = 3;
53
+ optional Point3d pitch_roll_yaw = 4;
54
+ repeated double rotation_c2w = 5; //size = 9
55
+ optional double relative_altitude = 6;
56
+ optional double absolute_altitude = 7;
57
+
58
+ enum BandType {
59
+ RGB = 1;
60
+ Red = 2;
61
+ Green= 3;
62
+ Blue = 4;
63
+ NIR = 5;
64
+ RedEdge = 6;
65
+ };
66
+ optional BandType band_type = 8;
67
+ optional string capture_uuid = 9;
68
+ optional string drone_id = 10;
69
+ // 多光谱影像相关参数
70
+ optional double black_current = 11;
71
+ repeated double vignetting_data = 12; //size = 6
72
+ optional double sensor_gain = 13;
73
+ optional double sensor_gain_adjustment = 14;
74
+ optional double exposure_time = 15;
75
+ optional double irradiance = 16;
76
+
77
+ optional string camera_model = 17;
78
+ required int32 width = 18;
79
+ required int32 height = 19;
80
+ optional bool dewrap_flag = 20;
81
+ optional int32 rtk_flag = 21;
82
+ optional uint64 capture_time = 22;
83
+ optional uint32 focal_length_in_35mm = 23;
84
+ optional uint32 camera_id = 24;
85
+ repeated double pre_calib_parameters = 25;
86
+ optional bool position_constant = 26;
87
+ optional double f_number = 27;
88
+ optional double ios_speed_rating = 28;
89
+ }
90
+
91
+ message ControlPoint {
92
+ required string ctrl_id = 1;
93
+ required uint32 usage = 2; // 0 for control point, 1 for check point, 2 for no usage
94
+ required Point3d point = 3;
95
+ required Point3d sigma = 4;
96
+ repeated uint32 img_id = 5;
97
+ repeated Point2f observation = 6;
98
+ }
99
+
100
+ message ControlPointGroup {
101
+ repeated ControlPoint points = 1;
102
+ required CoordinateSystem coordiante_system = 2;
103
+ }
104
+
105
+ message MVSCamera {
106
+ enum DistortionType {
107
+ OPENCV_K1K2K3P1P2 = 1;
108
+ UnKnow = 2;
109
+ };
110
+ required DistortionType distortion_type = 1;
111
+ optional int32 camera_id = 2;
112
+ repeated double camera_params = 3;
113
+ required int32 width = 4;
114
+ required int32 height = 5;
115
+ }
116
+
117
+ message MVSImage {
118
+ required uint32 img_id = 1;
119
+ optional string img_path = 2;
120
+ required uint32 camera_id = 3;
121
+ repeated double projection_matrix = 4;
122
+ required ImageRect image_rect = 5;
123
+ repeated double color_params = 6;
124
+ }
125
+
126
+ message MVSPoint {
127
+ required Point3d point = 1;
128
+ optional int32 rgb = 2; // (r << 16) | (g << 8) | b
129
+ }
130
+
131
+ message MVSObservation {
132
+ required float x = 1;
133
+ required float y = 2;
134
+ required int32 img_index = 3;
135
+ required int32 pnt_index = 4;
136
+ optional int32 rgb = 5; // (r << 16) | (g << 8) | b
137
+ }
138
+
139
+ message ImageMetaDataWithID {
140
+ required uint32 id=1;
141
+ required ImageMetaData meta_data=2;
142
+ }
143
+
144
+ message MVSBlock {
145
+ repeated MVSCamera camera = 1;
146
+ repeated MVSImage image = 2;
147
+ repeated MVSPoint point = 3;
148
+ required CoordinateSystem mvs_image_coordinate_system = 5;
149
+ required BoundingBox3d bounding_box = 6;
150
+ required BoundingBox3d tight_bounding_box = 7;
151
+ optional CoordinateSystem meta_data_coordinate_system = 9;
152
+ optional ControlPointGroup control_point_group = 10;
153
+ optional string version = 11;
154
+ }
155
+ `
@@ -0,0 +1,168 @@
1
+ import {mat3, vec3, vec2} from 'gl-matrix'
2
+ import satellite_geo_calc from 'satellite-geo-calc'
3
+ import { transformCoordinateToLngLat } from './transform'
4
+ import * as math from 'mathjs'
5
+
6
+ // 刺点预测
7
+ /**
8
+ *
9
+ * @param {*} cameraInfo.images 照片列表,包含{img_id, img_path, camera_id, image_rect, projection_matrix}
10
+ * @param {*} cameraInfo.cameras 相机列表,包含{camera_id, camera_params, width, height}
11
+ * @param {*} controlPoint 当前控制点,包含{id, coordinate, usage, observations}
12
+ */
13
+ export function predictObservations (cameraInfo, controlPoint, control_coordinate_system) {
14
+ let {observations} = controlPoint
15
+ if (observations.length > 1) {
16
+ return predictOptimize(cameraInfo, controlPoint)
17
+ } else {
18
+ return predictBeforeAT(cameraInfo, controlPoint, control_coordinate_system)
19
+ }
20
+ }
21
+
22
+ function predictBeforeAT (cameraInfo, controlPoint, control_coordinate_system) {
23
+ let {images, cameras, coordinateSystem} = cameraInfo
24
+ let camersMap = {}
25
+ cameras.forEach(c => camersMap[c.camera_id] = c)
26
+ let {coordinate} = controlPoint
27
+ let Xw = vec3.fromValues(0, 0, 0)
28
+ if (control_coordinate_system.type === 1) {
29
+ Xw = vec3.fromValues(coordinate[1], coordinate[1], coordinate[2])
30
+ } else {
31
+ let lngLat = transformCoordinateToLngLat(coordinate, control_coordinate_system)
32
+ let originalPoint = coordinateSystem.origin_point // enu的坐标原点
33
+ let Xenu = satellite_geo_calc.coordinateTransfer.lla_to_enu({ lon:lngLat[0], lat:lngLat[1], alt: lngLat[2] || 0}, {lon: originalPoint[0], lat: originalPoint[1], alt: originalPoint[2]})
34
+ Xw = vec3.fromValues(Xenu.x, Xenu.y, Xenu.z)
35
+ }
36
+ let predicts = {}
37
+ images.forEach(image => {
38
+ let {img_id, camera_id, projection_matrix, image_rect} = image
39
+ let parameters = camersMap[camera_id].camera_params
40
+ let {width, height} = image_rect
41
+ let P = projection_matrix
42
+ let R = mat3.fromValues(P[0], P[4], P[8], P[1], P[5], P[9], P[2], P[6], P[10]) // 旋转矩阵, 列主序
43
+ let T = vec3.fromValues(P[3], P[7], P[11]) // 平移向量
44
+ let K = mat3.fromValues(parameters[0], 0, 0, 0, parameters[1], 0, parameters[2], parameters[3], 1)
45
+ let ph = vec3.transformMat3(vec3.create(), vec3.add(vec3.create(), vec3.transformMat3(vec3.create(), Xw, R), T), K)
46
+ let pixelCoord = vec3.scale(vec3.create, ph, 1 / ph[2])
47
+ if (pixelCoord[0] > 0 && pixelCoord[0] < width && pixelCoord[1] > 0 && pixelCoord[1] < height) {
48
+ // 加上相机畸变
49
+ let p_vec = vec3.fromValues(pixelCoord[0], pixelCoord[1], 1)
50
+ let x_n = vec3.transformMat3(vec3.create(), p_vec, mat3.invert(mat3.create(), K))
51
+ let dist = distortPoint(x_n, parameters)
52
+ pixelCoord = vec3.transformMat3(vec3.create(), vec3.fromValues(...dist, 1), K)
53
+ predicts[img_id] = [pixelCoord[0], pixelCoord[1]]
54
+ image.predict = {
55
+ uv: [pixelCoord[0], pixelCoord[1]],
56
+ position: {left: pixelCoord[0] * 100 / width, top: pixelCoord[1] * 100 / height},
57
+ distance: Math.hypot(pixelCoord[0] - width / 2, pixelCoord[1] - height / 2)
58
+ }
59
+ } else {
60
+ image.predict = undefined
61
+ }
62
+ })
63
+ return predicts
64
+ }
65
+
66
+ function predictOptimize (cameraInfo, controlPoint) {
67
+ let {images, cameras} = cameraInfo
68
+ let {observations} = controlPoint
69
+ let camersMap = {}
70
+ cameras.forEach(c => camersMap[c.camera_id] = c)
71
+ let predicts = {}, observedImages = [] // 已经刺点的图像
72
+ let obsevedMap = new Map()
73
+ observations.forEach((d) => obsevedMap.set(d.id, d.uv))
74
+ images.forEach(image => { // 第一次循环,找出已经完成刺点的图像,后面需要用这些刺点坐标列方程
75
+ if (!obsevedMap.has(image.img_id)) {
76
+ image.observation = undefined
77
+ return
78
+ }
79
+ image.observation = {
80
+ id: image.img_id,
81
+ uv: obsevedMap.get(image.img_id)
82
+ }
83
+ observedImages.push(image)
84
+ })
85
+ // 根据已经刺的点计算控制点的空间坐标
86
+ let a = [], b = [] // A矩阵和B矩阵的数组,满足Ax + B = 0,其中x未待求的坐标值,行优先
87
+ observedImages.forEach(observedImage => {
88
+ let P_m = observedImage.projection_matrix
89
+ let parameters_m = camersMap[observedImage.camera_id].camera_params
90
+ let undistort_uv = undistortPoint(observedImage.observation.uv, parameters_m)
91
+ let R_m = mat3.fromValues(P_m[0], P_m[4], P_m[8], P_m[1], P_m[5], P_m[9], P_m[2], P_m[6], P_m[10]) // 旋转矩阵, 列主序
92
+ let t_m = vec3.fromValues(P_m[3], P_m[7], P_m[11]) // 平移向量
93
+ let K_m = mat3.fromValues(parameters_m[0], 0, 0, 0, parameters_m[1], 0, parameters_m[2], parameters_m[3], 1)
94
+ let K_m_invert = mat3.invert(mat3.create(), K_m) // K_m的逆矩阵
95
+ // 满足rX = l
96
+ let undistort_uv_norm = vec3.transformMat3(vec3.create(), undistort_uv, K_m_invert)
97
+ a.push([R_m[0] - R_m[2] * undistort_uv_norm[0], R_m[3] - R_m[5] * undistort_uv_norm[0], R_m[6] - R_m[8] * undistort_uv_norm[0]])
98
+ a.push([R_m[1] - R_m[2] * undistort_uv_norm[1], R_m[4] - R_m[5] * undistort_uv_norm[1], R_m[7] - R_m[8] * undistort_uv_norm[1]])
99
+ b.push(t_m[2] * undistort_uv_norm[0] - t_m[0], t_m[2] * undistort_uv_norm[1] - t_m[1])
100
+ })
101
+ let A = math.matrix(a)
102
+ let B = math.matrix(b)
103
+ let A_t = math.transpose(A)
104
+ let result = math.multiply(math.multiply(math.inv(math.multiply(A_t, A)), A_t), B)
105
+ let X = vec3.fromValues(result.get([0]), result.get([1]), result.get([2]))
106
+
107
+ images.forEach(image => { // 第二次循环,计算未刺点图像上的预测坐标
108
+ let {img_id, camera_id, projection_matrix, image_rect} = image
109
+ let parameters = camersMap[camera_id].camera_params
110
+ let {width, height} = image_rect
111
+ if (image.observation) { // 已经刺点,不需要预测
112
+ return image.predict = {
113
+ uv: image.observation.uv,
114
+ position: {left: image.observation.uv[0] * 100 / width, top: image.observation.uv[1] * 100 / height},
115
+ distance: Math.hypot(image.observation.uv[0] - width / 2, image.observation.uv[1] - height / 2)
116
+ }
117
+ }
118
+ let P_n = projection_matrix
119
+ let R_n = mat3.fromValues(P_n[0], P_n[4], P_n[8], P_n[1], P_n[5], P_n[9], P_n[2], P_n[6], P_n[10]) // 旋转矩阵, 列主序
120
+ let t_n = vec3.fromValues(P_n[3], P_n[7], P_n[11]) // 平移向量
121
+ let K_n = mat3.fromValues(parameters[0], 0, 0, 0, parameters[1], 0, parameters[2], parameters[3], 1)
122
+ let p = vec3.add(vec3.create(), vec3.transformMat3(vec3.create(), X, R_n), t_n)
123
+ let pixelCoord = vec3.transformMat3(vec3.create(), vec3.fromValues(p[0] / p[2], p[1] / p[2], 1), K_n)
124
+ if (pixelCoord[0] > 0 && pixelCoord[0] < width && pixelCoord[1] > 0 && pixelCoord[1] < height) {
125
+ let p_vec = vec3.fromValues(...pixelCoord, 1)
126
+ let x_n = vec3.transformMat3(vec3.create(), p_vec, mat3.invert(mat3.create(), K_n))
127
+ let dist = distortPoint(x_n, parameters)
128
+ pixelCoord = vec3.transformMat3(vec3.create(), vec3.fromValues(...dist, 1), K_n)
129
+ predicts[img_id] = [pixelCoord[0], pixelCoord[1]]
130
+ image.predict = {
131
+ uv: [pixelCoord[0], pixelCoord[1]],
132
+ position: {left: pixelCoord[0] * 100 / width, top: pixelCoord[1] * 100 / height},
133
+ distance: Math.hypot(pixelCoord[0] - width / 2, pixelCoord[1] - height / 2)
134
+ }
135
+ } else {
136
+ image.predict = undefined
137
+ }
138
+ })
139
+ return predicts
140
+ }
141
+
142
+ function undistortPoint (p, parameters) {
143
+ let K = mat3.fromValues(parameters[0], 0, 0, 0, parameters[1], 0, parameters[2], parameters[3], 1)
144
+ let p_vec = vec3.transformMat3(vec3.create(), vec3.fromValues(...p, 1), mat3.invert(mat3.create(), K)) // 归一化以后的坐标
145
+ let iterators = 100
146
+ let epsilon = 1e-10
147
+ let undistort = vec2.fromValues(p_vec[0], p_vec[1])
148
+ let dist = vec2.subtract(vec2.create(), distortPoint(undistort, parameters), undistort)
149
+ let diff = vec2.subtract(vec2.create(), vec2.add(vec2.create(), undistort, dist), p_vec)
150
+ while((Math.abs(diff[0]) + Math.abs(diff[1])) > epsilon && iterators-- > 0) {
151
+ undistort = vec2.subtract(vec2.create(), p_vec, dist)
152
+ dist = vec2.subtract(vec2.create(), distortPoint(undistort, parameters), undistort)
153
+ diff = vec2.subtract(vec2.create(), vec2.add(vec2.create(), undistort, dist), p_vec)
154
+ }
155
+ return vec3.transformMat3(vec3.create(), vec3.fromValues(...undistort, 1), K)
156
+ }
157
+
158
+ function distortPoint (p, parameters) {
159
+ let x3 = [p[0], p[1]]
160
+ let r2 = x3[0] * x3[0] + x3[1] * x3[1]
161
+ let r4 = Math.pow(r2, 2)
162
+ let r6 = Math.pow(r2, 3)
163
+ let x3_ = [
164
+ x3[0] * (1 + parameters[4] * r2 + parameters[5] * r4 + parameters[6] * r6) + 2 * parameters[7] * x3[0] * x3[1] + parameters[8] * (r2 + 2 * x3[0] * x3[0]),
165
+ x3[1] * (1 + parameters[4] * r2 + parameters[5] * r4 + parameters[6] * r6) + 2 * parameters[8] * x3[0] * x3[1] + parameters[7] * (r2 + 2 * x3[1] * x3[1])
166
+ ]
167
+ return vec2.fromValues(...x3_)
168
+ }
@@ -0,0 +1,33 @@
1
+ export default {
2
+ 0x0000: 'GPSVersionID',
3
+ 0x0001: 'GPSLatitudeRef',
4
+ 0x0002: 'GPSLatitude',
5
+ 0x0003: 'GPSLongitudeRef',
6
+ 0x0004: 'GPSLongitude',
7
+ 0x0005: 'GPSAltitudeRef',
8
+ 0x0006: 'GPSAltitude',
9
+ 0x0007: 'GPSTimeStamp',
10
+ 0x0008: 'GPSSatellites',
11
+ 0x0009: 'GPSStatus',
12
+ 0x000A: 'GPSMeasureMode',
13
+ 0x000B: 'GPSDOP',
14
+ 0x000C: 'GPSSpeedRef',
15
+ 0x000D: 'GPSSpeed',
16
+ 0x000E: 'GPSTrackRef',
17
+ 0x000F: 'GPSTrack',
18
+ 0x0010: 'GPSImgDirectionRef',
19
+ 0x0011: 'GPSImgDirection',
20
+ 0x0012: 'GPSMapDatum',
21
+ 0x0013: 'GPSDestLatitudeRef',
22
+ 0x0014: 'GPSDestLatitude',
23
+ 0x0015: 'GPSDestLongitudeRef',
24
+ 0x0016: 'GPSDestLongitude',
25
+ 0x0017: 'GPSDestBearingRef',
26
+ 0x0018: 'GPSDestBearing',
27
+ 0x0019: 'GPSDestDistanceRef',
28
+ 0x001A: 'GPSDestDistance',
29
+ 0x001B: 'GPSProcessingMethod',
30
+ 0x001C: 'GPSAreaInformation',
31
+ 0x001D: 'GPSDateStamp',
32
+ 0x001E: 'GPSDifferential'
33
+ }