@ngageoint/mage.service 6.2.9 → 6.2.10-beta.2

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 (139) hide show
  1. package/lib/@types/express/index.d.ts +1 -1
  2. package/lib/adapters/icons/adapters.icons.db.mongoose.js.map +1 -1
  3. package/lib/adapters/observations/adapters.observations.controllers.web.js +2 -2
  4. package/lib/adapters/observations/adapters.observations.controllers.web.js.map +1 -1
  5. package/lib/adapters/observations/adapters.observations.dto.ecma404-json.d.ts +1 -1
  6. package/lib/adapters/observations/adapters.observations.dto.ecma404-json.js +1 -1
  7. package/lib/api/attachment.d.ts +3 -0
  8. package/lib/api/attachment.d.ts.map +1 -1
  9. package/lib/api/attachment.js +3 -0
  10. package/lib/api/attachment.js.map +1 -1
  11. package/lib/api/icon.d.ts +51 -20
  12. package/lib/api/icon.js +1 -1
  13. package/lib/api/icon.js.map +1 -1
  14. package/lib/api/location.d.ts +1 -3
  15. package/lib/api/location.d.ts.map +1 -1
  16. package/lib/app.api/observations/app.api.observations.js +1 -1
  17. package/lib/app.api/observations/app.api.observations.js.map +1 -1
  18. package/lib/app.d.ts +2 -2
  19. package/lib/app.d.ts.map +1 -1
  20. package/lib/app.impl/observations/app.impl.observations.d.ts.map +1 -1
  21. package/lib/app.impl/observations/app.impl.observations.js +2 -1
  22. package/lib/app.impl/observations/app.impl.observations.js.map +1 -1
  23. package/lib/app.js +5 -3
  24. package/lib/app.js.map +1 -1
  25. package/lib/authentication/saml.d.ts.map +1 -1
  26. package/lib/authentication/saml.js +15 -14
  27. package/lib/authentication/saml.js.map +1 -1
  28. package/lib/dist-package.json +45 -45
  29. package/lib/docs/auth/local.yaml +1 -2
  30. package/lib/entities/authorization/entities.permissions.d.ts +7 -1
  31. package/lib/entities/authorization/entities.permissions.d.ts.map +1 -1
  32. package/lib/entities/authorization/entities.permissions.js +7 -2
  33. package/lib/entities/authorization/entities.permissions.js.map +1 -1
  34. package/lib/entities/entities.global.d.ts +6 -0
  35. package/lib/entities/entities.global.d.ts.map +1 -1
  36. package/lib/entities/entities.global.js +12 -1
  37. package/lib/entities/entities.global.js.map +1 -1
  38. package/lib/entities/events/entities.events.d.ts.map +1 -1
  39. package/lib/entities/events/entities.events.forms.d.ts +9 -1
  40. package/lib/entities/events/entities.events.forms.d.ts.map +1 -1
  41. package/lib/entities/events/entities.events.forms.js +24 -3
  42. package/lib/entities/events/entities.events.forms.js.map +1 -1
  43. package/lib/entities/events/entities.events.js.map +1 -1
  44. package/lib/entities/locations/entities.locations.d.ts +30 -0
  45. package/lib/entities/locations/entities.locations.d.ts.map +1 -0
  46. package/lib/entities/locations/entities.locations.js +3 -0
  47. package/lib/entities/locations/entities.locations.js.map +1 -0
  48. package/lib/entities/observations/entities.observations.d.ts +4 -4
  49. package/lib/entities/observations/entities.observations.d.ts.map +1 -1
  50. package/lib/entities/observations/entities.observations.js +16 -15
  51. package/lib/entities/observations/entities.observations.js.map +1 -1
  52. package/lib/environment/env.js +1 -1
  53. package/lib/environment/env.js.map +1 -1
  54. package/lib/export/csv.d.ts +19 -9
  55. package/lib/export/csv.d.ts.map +1 -1
  56. package/lib/export/csv.js +255 -231
  57. package/lib/export/csv.js.map +1 -1
  58. package/lib/export/exporter.d.ts +37 -10
  59. package/lib/export/exporter.d.ts.map +1 -1
  60. package/lib/export/exporter.js +67 -35
  61. package/lib/export/exporter.js.map +1 -1
  62. package/lib/export/geojson.d.ts +9 -8
  63. package/lib/export/geojson.d.ts.map +1 -1
  64. package/lib/export/geojson.js +162 -143
  65. package/lib/export/geojson.js.map +1 -1
  66. package/lib/export/geopackage.d.ts +29 -23
  67. package/lib/export/geopackage.d.ts.map +1 -1
  68. package/lib/export/geopackage.js +659 -589
  69. package/lib/export/geopackage.js.map +1 -1
  70. package/lib/export/index.d.ts +17 -0
  71. package/lib/export/index.d.ts.map +1 -0
  72. package/lib/export/index.js +28 -0
  73. package/lib/export/index.js.map +1 -0
  74. package/lib/export/kml.d.ts +11 -8
  75. package/lib/export/kml.d.ts.map +1 -1
  76. package/lib/export/kml.js +152 -106
  77. package/lib/export/kml.js.map +1 -1
  78. package/lib/export/kmlWriter.d.ts +23 -22
  79. package/lib/export/kmlWriter.d.ts.map +1 -1
  80. package/lib/export/kmlWriter.js +258 -181
  81. package/lib/export/kmlWriter.js.map +1 -1
  82. package/lib/express.js +2 -4
  83. package/lib/express.js.map +1 -1
  84. package/lib/migrations/030-saml-settings.d.ts +4 -0
  85. package/lib/migrations/030-saml-settings.d.ts.map +1 -0
  86. package/lib/migrations/030-saml-settings.js +96 -0
  87. package/lib/migrations/030-saml-settings.js.map +1 -0
  88. package/lib/models/authentication.js +1 -1
  89. package/lib/models/authenticationconfiguration.js +0 -1
  90. package/lib/models/authenticationconfiguration.js.map +1 -1
  91. package/lib/models/event.d.ts +4 -2
  92. package/lib/models/event.js +1 -1
  93. package/lib/models/event.js.map +1 -1
  94. package/lib/models/export.d.ts +56 -17
  95. package/lib/models/export.d.ts.map +1 -1
  96. package/lib/models/export.js.map +1 -1
  97. package/lib/models/icon.d.ts +24 -9
  98. package/lib/models/icon.d.ts.map +1 -1
  99. package/lib/models/icon.js +3 -6
  100. package/lib/models/icon.js.map +1 -1
  101. package/lib/models/location.d.ts +40 -8
  102. package/lib/models/location.d.ts.map +1 -1
  103. package/lib/models/location.js +2 -0
  104. package/lib/models/location.js.map +1 -1
  105. package/lib/models/observation.d.ts +27 -0
  106. package/lib/models/observation.js.map +1 -1
  107. package/lib/models/user.d.ts +3 -0
  108. package/lib/models/user.js +1 -1
  109. package/lib/provision/index.js +1 -1
  110. package/lib/provision/index.js.map +1 -1
  111. package/lib/routes/authenticationconfigurations.d.ts.map +1 -1
  112. package/lib/routes/authenticationconfigurations.js +4 -5
  113. package/lib/routes/authenticationconfigurations.js.map +1 -1
  114. package/lib/routes/events.d.ts.map +1 -1
  115. package/lib/routes/events.js +19 -10
  116. package/lib/routes/events.js.map +1 -1
  117. package/lib/routes/exports.d.ts +3 -2
  118. package/lib/routes/exports.d.ts.map +1 -1
  119. package/lib/routes/exports.js +121 -79
  120. package/lib/routes/exports.js.map +1 -1
  121. package/lib/routes/imports.d.ts.map +1 -1
  122. package/lib/routes/imports.js +4 -2
  123. package/lib/routes/imports.js.map +1 -1
  124. package/lib/routes/layers.d.ts.map +1 -1
  125. package/lib/routes/layers.js +11 -45
  126. package/lib/routes/layers.js.map +1 -1
  127. package/lib/routes/locations.d.ts.map +1 -1
  128. package/lib/routes/locations.js +7 -3
  129. package/lib/routes/locations.js.map +1 -1
  130. package/lib/routes/routes.types.d.ts +9 -0
  131. package/lib/routes/setup.js +1 -1
  132. package/lib/routes/setup.js.map +1 -1
  133. package/lib/tsconfig.tsbuildinfo +1 -1
  134. package/npm-shrinkwrap.json +4411 -7493
  135. package/package.json +45 -45
  136. package/lib/export/exporterFactory.d.ts +0 -11
  137. package/lib/export/exporterFactory.d.ts.map +0 -1
  138. package/lib/export/exporterFactory.js +0 -17
  139. package/lib/export/exporterFactory.js.map +0 -1
@@ -1,4 +1,27 @@
1
1
  'use strict';
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,528 +31,701 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
31
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
32
  });
10
33
  };
11
- const { RelationType } = require('@ngageoint/geopackage/dist/lib/extension/relatedTables/relationType');
12
- const { EnvelopeBuilder } = require('@ngageoint/geopackage/dist/lib/geom/envelopeBuilder');
13
- const util = require('util'), fs = require('fs'), api = require('../api'), archiver = require('archiver'), moment = require('moment'), log = require('winston'), path = require('path'), Exporter = require('./exporter'), GeoPackageAPI = require('@ngageoint/geopackage'), environment = require('../environment/env'), os = require('os'), wkx = require('wkx'), User = require('../models/user');
14
- const attachmentBase = environment.attachmentBaseDirectory;
15
- const pathToGeoPackageModule = path.resolve(path.dirname(require.resolve('@ngageoint/geopackage/package.json')));
16
- GeoPackageAPI.setCanvasKitWasmLocateFile(file => `${pathToGeoPackageModule}/dist/canvaskit/${file}`);
17
- function GeoPackage(options) {
18
- GeoPackage.super_.call(this, options);
19
- this.iconMap = {};
20
- }
21
- util.inherits(GeoPackage, Exporter);
22
- module.exports = GeoPackage;
23
- GeoPackage.prototype.export = function (streamable) {
24
- return __awaiter(this, void 0, void 0, function* () {
25
- log.info('Export the GeoPackage');
26
- const downloadedFileName = 'mage-' + this._event.name;
27
- const archive = archiver('zip');
28
- archive.pipe(streamable);
29
- try {
30
- const filePath = yield this.createGeoPackageFile();
31
- const gp = yield GeoPackageAPI.GeoPackageAPI.create(filePath);
32
- yield this.createUserTable(gp);
33
- yield this.createUserFeatureTableStyles(gp);
34
- if (this._filter.exportObservations) {
35
- yield this.addFormDataToGeoPackage(gp);
36
- yield this.createFormAttributeTables(gp);
37
- yield this.createObservationTable(gp);
38
- yield this.createObservationFeatureTableStyles(gp);
39
- yield this.addObservationsToGeoPackage(gp);
40
- }
41
- if (this._filter.exportLocations) {
42
- yield this.addLocationsToGeoPackage(gp);
43
- }
44
- log.info(`export geopackage created: ${filePath}`);
45
- archive.append(fs.createReadStream(filePath), { name: downloadedFileName + '.gpkg' });
46
- archive.on('end', function () {
47
- log.info(`removing temporary export geopackage file ${filePath}`);
48
- fs.unlink(filePath, function () {
49
- gp.close();
50
- });
51
- });
52
- archive.finalize();
53
- }
54
- catch (err) {
55
- log.error(`error exporting geopackage`, err);
56
- throw err;
57
- }
58
- });
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
59
36
  };
60
- GeoPackage.prototype.createGeoPackageFile = function () {
61
- log.info('Create GeoPackage File');
62
- const filename = moment().format('YYYMMDD_hhmmssSSS') + '.gpkg';
63
- const filePath = path.join(os.tmpdir(), filename);
64
- return new Promise(function (resolve, reject) {
65
- fs.unlink(filePath, function () {
66
- fs.mkdir(path.dirname(filePath), function () {
67
- fs.open(filePath, 'w', function (err) {
68
- if (err)
69
- return reject(err);
70
- resolve(filePath);
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.GeoPackage = void 0;
39
+ const relationType_1 = require("@ngageoint/geopackage/dist/lib/extension/relatedTables/relationType");
40
+ const envelopeBuilder_1 = require("@ngageoint/geopackage/dist/lib/geom/envelopeBuilder");
41
+ const GPKG = __importStar(require("@ngageoint/geopackage"));
42
+ const geopackage_1 = require("@ngageoint/geopackage");
43
+ const util_1 = __importDefault(require("util"));
44
+ const fs_1 = __importDefault(require("fs"));
45
+ const promises_1 = __importDefault(require("fs/promises"));
46
+ const archiver_1 = __importDefault(require("archiver"));
47
+ const moment_1 = __importDefault(require("moment"));
48
+ const os_1 = __importDefault(require("os"));
49
+ const path_1 = __importDefault(require("path"));
50
+ const wkx_1 = __importDefault(require("wkx"));
51
+ const exporter_1 = require("./exporter");
52
+ const api_1 = __importDefault(require("../api"));
53
+ const env_1 = __importDefault(require("../environment/env"));
54
+ const user_1 = __importDefault(require("../models/user"));
55
+ // TODO: we really need to revamp our logging
56
+ const logger = require('../logger');
57
+ const log = ['debug', 'info', 'warn', 'error', 'log'].reduce((log, methodName) => {
58
+ const logMethod = logger[methodName];
59
+ return Object.assign(Object.assign({}, log), { [methodName]: (...args) => logMethod('[export:geopackage]', ...args) });
60
+ }, {});
61
+ const attachmentBase = env_1.default.attachmentBaseDirectory;
62
+ class GeoPackage extends exporter_1.Exporter {
63
+ constructor() {
64
+ super(...arguments);
65
+ this.iconCache = new IconTreeCache();
66
+ this.observationStyles = null;
67
+ }
68
+ export(streamable) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ log.info(`export geopackage for event ${this._event.id} - ${this._event.name}:\n`, this._filter);
71
+ const downloadedFileName = 'mage-' + this._event.name;
72
+ const archive = (0, archiver_1.default)('zip');
73
+ archive.pipe(streamable);
74
+ try {
75
+ const filePath = yield createGeoPackageFile();
76
+ const gp = yield geopackage_1.GeoPackageAPI.create(filePath);
77
+ yield this.createUserTable(gp);
78
+ yield createUserFeatureTableStyles(gp);
79
+ if (this._filter.exportObservations) {
80
+ yield this.addFormDataToGeoPackage(gp);
81
+ yield this.createFormAttributeTables(gp);
82
+ yield this.createObservationTable(gp);
83
+ this.observationStyles = yield this.createObservationFeatureTableStyles(gp);
84
+ yield this.addObservationsToGeoPackage(gp);
85
+ }
86
+ if (this._filter.exportLocations) {
87
+ yield this.addLocationsToGeoPackage(gp);
88
+ }
89
+ log.info(`export geopackage created: ${filePath}`);
90
+ archive.append(fs_1.default.createReadStream(filePath), { name: downloadedFileName + '.gpkg' });
91
+ archive.on('end', () => {
92
+ log.info(`removing temporary export geopackage file ${filePath}`);
93
+ fs_1.default.unlink(filePath, (err) => {
94
+ if (err) {
95
+ console.warn('error removing temporary geopackage', filePath);
96
+ }
97
+ gp.close();
98
+ });
71
99
  });
72
- });
73
- });
74
- });
75
- };
76
- GeoPackage.prototype.createObservationTable = function (geopackage) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- log.info('Create Observation Table');
79
- const columns = [];
80
- // TODO columns should be the same as KML file
81
- columns.push({
82
- name: 'lastModified',
83
- dataType: 'DATETIME'
84
- });
85
- columns.push({
86
- name: 'timestamp',
87
- dataType: 'DATETIME'
88
- });
89
- columns.push({
90
- name: 'mageId',
91
- dataType: 'TEXT'
92
- });
93
- columns.push({
94
- name: 'userId',
95
- dataType: 'TEXT'
96
- });
97
- columns.push({
98
- name: 'deviceId',
99
- dataType: 'TEXT'
100
- });
101
- columns.push({
102
- name: 'createdAt',
103
- dataType: 'DATETIME'
104
- });
105
- columns.push({
106
- name: 'primaryField',
107
- dataType: 'TEXT'
108
- });
109
- columns.push({
110
- name: 'variantField',
111
- dataType: 'TEXT'
112
- });
113
- yield geopackage.createFeatureTableFromProperties('Observations', columns);
114
- return geopackage;
115
- });
116
- };
117
- GeoPackage.prototype.createAttachmentTable = function (geopackage) {
118
- log.info('Create Attachment Table');
119
- const columns = [{
120
- name: "name",
121
- dataType: "TEXT"
122
- }, {
123
- name: "size",
124
- dataType: "REAL"
125
- }];
126
- return geopackage.createMediaTable('Attachments', columns);
127
- };
128
- GeoPackage.prototype.addUserToUsersTable = function (geopackage, user, usersLastLocation, zoomToEnvelope) {
129
- return __awaiter(this, void 0, void 0, function* () {
130
- log.info(`add user ${user.username} to users table`);
131
- const geoJson = {
132
- type: 'Feature',
133
- geometry: usersLastLocation.geometry,
134
- properties: {
135
- timestamp: usersLastLocation.properties.timestamp,
136
- username: user.username,
137
- displayName: user.displayName,
138
- email: user.email,
139
- phones: user.phones.join(', '),
140
- userId: user._id.toString()
100
+ archive.finalize();
101
+ }
102
+ catch (err) {
103
+ log.error(`error exporting geopackage`, err);
104
+ throw err;
141
105
  }
142
- };
143
- const userRowId = geopackage.addGeoJSONFeatureToGeoPackage(geoJson, 'Users');
144
- const iconPath = path.join(environment.userBaseDirectory, user._id.toString(), 'icon');
145
- let iconBuffer = null;
146
- try {
147
- iconBuffer = yield util.promisify(fs.readFile)(iconPath);
148
- }
149
- catch (err) {
150
- log.error(`error reading reading user icon for geopackage export: ${iconPath}`, err);
151
- return void (0);
152
- }
153
- const featureTableStyles = new GeoPackageAPI.FeatureTableStyles(geopackage, 'Users');
154
- const iconRow = featureTableStyles.getIconDao().newRow();
155
- iconRow.data = iconBuffer;
156
- iconRow.contentType = 'image/png';
157
- iconRow.name = user.username;
158
- iconRow.description = `Icon for user ${user.username}`;
159
- iconRow.width = 20;
160
- iconRow.anchorU = 0.5;
161
- iconRow.anchorV = 1.0;
162
- featureTableStyles.setIconDefault(userRowId, iconRow);
163
- const featureDao = geopackage.getFeatureDao('Users');
164
- const rtreeIndex = new GeoPackageAPI.RTreeIndex(geopackage, featureDao);
165
- rtreeIndex.create();
166
- if (zoomToEnvelope) {
167
- this.setContentBounds(geopackage, featureDao, zoomToEnvelope);
168
- }
169
- });
170
- };
171
- GeoPackage.prototype.createLocationTableForUser = function (geopackage, userId) {
172
- return __awaiter(this, void 0, void 0, function* () {
173
- const columns = [];
174
- columns.push({
175
- name: 'mageId',
176
- dataType: 'TEXT'
177
- });
178
- columns.push({
179
- name: 'userId',
180
- dataType: 'TEXT'
181
- });
182
- columns.push({
183
- name: 'timestamp',
184
- dataType: 'DATETIME'
185
- });
186
- columns.push({
187
- name: 'deviceId',
188
- dataType: 'TEXT'
189
106
  });
190
- columns.push({
191
- name: 'accuracy',
192
- dataType: 'REAL'
107
+ }
108
+ createObservationTable(geopackage) {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ log.info('create observation table');
111
+ const columns = [];
112
+ // TODO columns should be the same as KML file
113
+ columns.push({
114
+ name: 'lastModified',
115
+ dataType: 'DATETIME'
116
+ });
117
+ columns.push({
118
+ name: 'timestamp',
119
+ dataType: 'DATETIME'
120
+ });
121
+ columns.push({
122
+ name: 'mageId',
123
+ dataType: 'TEXT'
124
+ });
125
+ columns.push({
126
+ name: 'userId',
127
+ dataType: 'TEXT'
128
+ });
129
+ columns.push({
130
+ name: 'deviceId',
131
+ dataType: 'TEXT'
132
+ });
133
+ columns.push({
134
+ name: 'createdAt',
135
+ dataType: 'DATETIME'
136
+ });
137
+ columns.push({
138
+ name: 'primaryField',
139
+ dataType: 'TEXT'
140
+ });
141
+ columns.push({
142
+ name: 'variantField',
143
+ dataType: 'TEXT'
144
+ });
145
+ yield geopackage.createFeatureTableFromProperties('Observations', columns);
193
146
  });
194
- yield geopackage.createFeatureTableFromProperties('Locations_' + userId, columns);
195
- return geopackage;
196
- });
197
- };
198
- GeoPackage.prototype.addLocationsToGeoPackage = function (geopackage) {
199
- return __awaiter(this, void 0, void 0, function* () {
200
- log.info('Requesting locations from DB');
201
- const startDate = this._filter.startDate ? moment(this._filter.startDate) : null;
202
- const endDate = this._filter.endDate ? moment(this._filter.endDate) : null;
203
- const cursor = this.requestLocations({ startDate: startDate, endDate: endDate, stream: true });
204
- let numLocations = 0;
205
- let user = null;
206
- let userLastLocation = null;
207
- let zoomToEnvelope;
208
- return cursor.eachAsync((location) => __awaiter(this, void 0, void 0, function* () {
209
- if (!user || user._id.toString() !== location.userId.toString()) {
210
- if (zoomToEnvelope) {
211
- //Switching user, so update location
212
- const featureDao = geopackage.getFeatureDao('Locations_' + user._id.toString());
213
- this.setContentBounds(geopackage, featureDao, zoomToEnvelope);
214
- yield this.addUserToUsersTable(geopackage, user, userLastLocation, zoomToEnvelope);
215
- }
216
- zoomToEnvelope = null;
217
- user = yield User.getUserById(location.userId);
218
- yield this.createLocationTableForUser(geopackage, location.userId.toString());
219
- }
220
- zoomToEnvelope = this.calculateBounds(location.geometry, zoomToEnvelope);
221
- userLastLocation = location;
222
- const geojson = {
147
+ }
148
+ createAttachmentTable(geopackage) {
149
+ log.info('create attachment table');
150
+ const columns = [{
151
+ name: "name",
152
+ dataType: "TEXT"
153
+ }, {
154
+ name: "size",
155
+ dataType: "REAL"
156
+ }];
157
+ geopackage.createMediaTable('Attachments', columns);
158
+ }
159
+ addUserToUsersTable(geopackage, user, usersLastLocation, zoomToEnvelope) {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ log.info(`add user ${user.username} to users table`);
162
+ const feature = {
223
163
  type: 'Feature',
224
- geometry: location.geometry,
225
- properties: location.properties
164
+ geometry: usersLastLocation.geometry,
165
+ properties: {
166
+ timestamp: usersLastLocation.properties.timestamp,
167
+ username: user.username,
168
+ displayName: user.displayName,
169
+ email: user.email,
170
+ phones: user.phones.join(', '),
171
+ userId: user._id.toString()
172
+ }
226
173
  };
227
- geojson.properties.mageId = location._id.toString();
228
- geojson.properties.userId = location.userId.toString();
229
- geojson.properties.deviceId = location.properties.deviceId.toString();
230
- if (geojson.properties.id) {
231
- delete geojson.properties.id;
174
+ const userRowId = geopackage.addGeoJSONFeatureToGeoPackage(feature, 'Users');
175
+ const iconPath = user.icon.relativePath ? path_1.default.join(env_1.default.userBaseDirectory, user.icon.relativePath) : null;
176
+ if (iconPath) {
177
+ const featureTableStyles = new GPKG.FeatureTableStyles(geopackage, 'Users');
178
+ const iconRow = featureTableStyles.getIconDao().newRow();
179
+ try {
180
+ const iconBuffer = yield promises_1.default.readFile(iconPath);
181
+ iconRow.data = iconBuffer;
182
+ iconRow.contentType = 'image/png';
183
+ iconRow.name = user.username;
184
+ iconRow.description = `Icon for user ${user.username}`;
185
+ iconRow.width = 20;
186
+ iconRow.anchorU = 0.5;
187
+ iconRow.anchorV = 1.0;
188
+ featureTableStyles.setIconDefault(userRowId, iconRow);
189
+ }
190
+ catch (err) {
191
+ log.error(`error reading user icon for geopackage export: ${iconPath}`, err);
192
+ return void (0);
193
+ }
232
194
  }
233
- yield geopackage.addGeoJSONFeatureToGeoPackage(geojson, 'Locations_' + location.userId.toString());
234
- numLocations++;
235
- })).then(() => __awaiter(this, void 0, void 0, function* () {
236
- if (cursor)
237
- cursor.close;
238
- if (zoomToEnvelope && user) {
239
- //Process the last user, since it was missed in the loop above
240
- const featureDao = geopackage.getFeatureDao('Locations_' + user._id.toString());
241
- this.setContentBounds(geopackage, featureDao, zoomToEnvelope);
242
- yield this.addUserToUsersTable(geopackage, user, userLastLocation, zoomToEnvelope);
195
+ const featureDao = geopackage.getFeatureDao('Users');
196
+ const rtreeIndex = new GPKG.RTreeIndex(geopackage, featureDao);
197
+ rtreeIndex.create();
198
+ if (zoomToEnvelope) {
199
+ setContentBounds(geopackage, featureDao, zoomToEnvelope);
243
200
  }
244
- log.info('Successfully wrote ' + numLocations + ' locations to Geopackage');
245
- return geopackage;
246
- ;
247
- })).catch(err => { log.warn(err); });
248
- });
249
- };
250
- GeoPackage.prototype.createFormAttributeTables = function (geopackage) {
251
- return __awaiter(this, void 0, void 0, function* () {
252
- log.info('Create Form Attribute Tables');
253
- const formIds = Object.keys(this._event.formMap);
254
- for (let i = 0; i < formIds.length; i++) {
255
- const formId = formIds[i];
201
+ });
202
+ }
203
+ createLocationTableForUser(geopackage, userId) {
204
+ return __awaiter(this, void 0, void 0, function* () {
256
205
  const columns = [];
257
- const form = this._event.formMap[formId];
258
- if (form.primaryField) {
259
- columns.push({
260
- name: 'primaryField',
261
- dataType: 'TEXT'
262
- });
263
- }
264
- if (form.variantField) {
265
- columns.push({
266
- name: 'variantField',
267
- dataType: 'TEXT'
268
- });
269
- }
270
206
  columns.push({
271
- name: 'formId',
272
- dataType: 'INTEGER',
273
- default: formId
207
+ name: 'mageId',
208
+ dataType: 'TEXT'
209
+ });
210
+ columns.push({
211
+ name: 'userId',
212
+ dataType: 'TEXT'
274
213
  });
275
- for (let i = 0; i < form.fields.length; i++) {
276
- const field = form.fields[i];
214
+ columns.push({
215
+ name: 'timestamp',
216
+ dataType: 'DATETIME'
217
+ });
218
+ columns.push({
219
+ name: 'deviceId',
220
+ dataType: 'TEXT'
221
+ });
222
+ columns.push({
223
+ name: 'accuracy',
224
+ dataType: 'REAL'
225
+ });
226
+ yield geopackage.createFeatureTableFromProperties('Locations_' + userId, columns);
227
+ });
228
+ }
229
+ addLocationsToGeoPackage(geopackage) {
230
+ return __awaiter(this, void 0, void 0, function* () {
231
+ log.info('fetching locations');
232
+ const { startDate, endDate } = this._filter;
233
+ const cursor = this.requestLocations({ startDate, endDate });
234
+ let numLocations = 0;
235
+ let user = null;
236
+ let userLastLocation = null;
237
+ let zoomToEnvelope = null;
238
+ return cursor.eachAsync((location) => __awaiter(this, void 0, void 0, function* () {
239
+ var _a;
240
+ if (!user || user._id.toString() !== location.userId.toString()) {
241
+ if (zoomToEnvelope) {
242
+ // Switching user, so update location
243
+ const featureDao = geopackage.getFeatureDao('Locations_' + user._id.toString());
244
+ setContentBounds(geopackage, featureDao, zoomToEnvelope);
245
+ yield this.addUserToUsersTable(geopackage, user, userLastLocation, zoomToEnvelope);
246
+ }
247
+ zoomToEnvelope = null;
248
+ user = yield user_1.default.getUserById(location.userId);
249
+ yield this.createLocationTableForUser(geopackage, location.userId.toString());
250
+ }
251
+ zoomToEnvelope = calculateBounds(location.geometry, zoomToEnvelope);
252
+ userLastLocation = location;
253
+ const properties = location.properties || {};
254
+ const feature = {
255
+ type: 'Feature',
256
+ geometry: location.geometry,
257
+ properties
258
+ };
259
+ feature.properties.mageId = location._id.toString();
260
+ feature.properties.userId = (_a = location.userId) === null || _a === void 0 ? void 0 : _a.toString();
261
+ feature.properties.deviceId = properties.deviceId ? properties.deviceId.toString() : undefined;
262
+ if (feature.properties.id) {
263
+ delete feature.properties.id;
264
+ }
265
+ yield geopackage.addGeoJSONFeatureToGeoPackage(feature, 'Locations_' + location.userId.toString());
266
+ numLocations++;
267
+ })).then(() => __awaiter(this, void 0, void 0, function* () {
268
+ if (cursor) {
269
+ cursor.close();
270
+ }
271
+ if (zoomToEnvelope && user) {
272
+ //Process the last user, since it was missed in the loop above
273
+ const featureDao = geopackage.getFeatureDao('Locations_' + user._id.toString());
274
+ setContentBounds(geopackage, featureDao, zoomToEnvelope);
275
+ yield this.addUserToUsersTable(geopackage, user, userLastLocation, zoomToEnvelope);
276
+ }
277
+ log.info(`wrote ${numLocations} locations to geopackage`);
278
+ }))
279
+ .catch(err => { log.warn(err); });
280
+ });
281
+ }
282
+ createFormAttributeTables(geopackage) {
283
+ return __awaiter(this, void 0, void 0, function* () {
284
+ log.info('create form attribute tables');
285
+ for (const form of this._event.forms) {
286
+ const columns = [];
287
+ if (form.primaryField) {
288
+ columns.push({
289
+ name: 'primaryField',
290
+ dataType: 'TEXT'
291
+ });
292
+ }
293
+ if (form.variantField) {
294
+ columns.push({
295
+ name: 'variantField',
296
+ dataType: 'TEXT'
297
+ });
298
+ }
277
299
  columns.push({
278
- dataColumn: {
279
- column_name: field.name,
280
- table_name: 'Form_' + formId,
281
- name: field.title,
282
- title: field.title
283
- },
284
- name: field.name,
285
- dataType: this.fieldTypeToGeoPackageType(field.type)
300
+ name: 'formId',
301
+ dataType: 'INTEGER',
302
+ default: form.id
286
303
  });
304
+ for (let i = 0; i < form.fields.length; i++) {
305
+ const field = form.fields[i];
306
+ columns.push({
307
+ dataColumn: {
308
+ column_name: field.name,
309
+ table_name: 'Form_' + form.id,
310
+ name: field.title,
311
+ title: field.title
312
+ },
313
+ name: field.name,
314
+ dataType: this.fieldTypeToGeoPackageType(field.type)
315
+ });
316
+ }
317
+ yield geopackage.createAttributesTableFromProperties('Form_' + form.id, columns);
287
318
  }
288
- yield geopackage.createAttributesTableFromProperties('Form_' + formId, columns);
319
+ });
320
+ }
321
+ fieldTypeToGeoPackageType(fieldType) {
322
+ switch (fieldType) {
323
+ case 'numberfield':
324
+ return 'INTEGER';
325
+ case 'attachment':
326
+ case 'textarea':
327
+ case 'textfield':
328
+ return 'TEXT';
329
+ default:
330
+ return 'TEXT';
289
331
  }
290
- return geopackage;
291
- });
292
- };
293
- GeoPackage.prototype.fieldTypeToGeoPackageType = function (fieldType) {
294
- switch (fieldType) {
295
- case 'numberfield':
296
- return 'INTEGER';
297
- case 'attachment':
298
- case 'textarea':
299
- case 'textfield':
300
- return 'TEXT';
301
- default:
302
- return 'TEXT';
303
332
  }
304
- };
305
- GeoPackage.prototype.createUserTable = function (geopackage) {
306
- return __awaiter(this, void 0, void 0, function* () {
307
- const columns = [];
308
- columns.push({
309
- name: 'username',
310
- dataType: 'TEXT'
311
- });
312
- columns.push({
313
- name: 'displayName',
314
- dataType: 'TEXT'
315
- });
316
- columns.push({
317
- name: 'email',
318
- dataType: 'TEXT'
319
- });
320
- columns.push({
321
- name: 'phones',
322
- dataType: 'TEXT'
323
- });
324
- columns.push({
325
- name: 'userId',
326
- dataType: 'TEXT'
327
- });
328
- columns.push({
329
- name: 'timestamp',
330
- dataType: 'DATETIME'
331
- });
332
- yield geopackage.createFeatureTableFromProperties('Users', columns);
333
- log.info('Create User Avatar Table');
334
- yield geopackage.createMediaTable('UserAvatars');
335
- return geopackage;
336
- });
337
- };
338
- GeoPackage.prototype.addFormDataToGeoPackage = function (geopackage) {
339
- return __awaiter(this, void 0, void 0, function* () {
340
- const columns = [];
341
- columns.push({
342
- name: 'formName',
343
- dataType: 'TEXT'
344
- });
345
- columns.push({
346
- name: 'primaryField',
347
- dataType: 'TEXT'
348
- });
349
- columns.push({
350
- name: 'variantField',
351
- dataType: 'TEXT'
352
- });
353
- columns.push({
354
- name: 'color',
355
- dataType: 'TEXT'
356
- });
357
- columns.push({
358
- name: 'formId',
359
- dataType: 'TEXT'
333
+ createUserTable(geopackage) {
334
+ return __awaiter(this, void 0, void 0, function* () {
335
+ const columns = [];
336
+ columns.push({
337
+ name: 'username',
338
+ dataType: 'TEXT'
339
+ });
340
+ columns.push({
341
+ name: 'displayName',
342
+ dataType: 'TEXT'
343
+ });
344
+ columns.push({
345
+ name: 'email',
346
+ dataType: 'TEXT'
347
+ });
348
+ columns.push({
349
+ name: 'phones',
350
+ dataType: 'TEXT'
351
+ });
352
+ columns.push({
353
+ name: 'userId',
354
+ dataType: 'TEXT'
355
+ });
356
+ columns.push({
357
+ name: 'timestamp',
358
+ dataType: 'DATETIME'
359
+ });
360
+ yield geopackage.createFeatureTableFromProperties('Users', columns);
361
+ log.info('create user avatar table');
362
+ yield geopackage.createMediaTable('UserAvatars', void (0) /* really is optional */);
360
363
  });
361
- yield geopackage.createAttributesTableFromProperties('Forms', columns);
362
- for (const formId in this._event.formMap) {
363
- const form = this._event.formMap[formId];
364
- const row = {
365
- formName: form.name,
366
- primaryField: form.primaryField,
367
- variantField: form.variantField,
368
- color: form.color,
369
- formId: formId
370
- };
371
- geopackage.addAttributeRow('Forms', row);
372
- }
373
- return geopackage;
374
- });
375
- };
376
- GeoPackage.prototype.addObservationsToGeoPackage = function (geopackage) {
377
- return __awaiter(this, void 0, void 0, function* () {
378
- log.info('Requesting locations from DB');
379
- this.createAttachmentTable(geopackage);
380
- const cursor = this.requestObservations(this._filter);
381
- let numObservations = 0;
382
- let zoomToEnvelope;
383
- return cursor.eachAsync((observation) => __awaiter(this, void 0, void 0, function* () {
384
- numObservations++;
385
- let primary;
386
- let variant;
387
- if (observation.properties.forms && observation.properties.forms.length > 0) {
388
- const observationFirstForm = observation.properties.forms[0];
389
- const form = this._event.formMap[observationFirstForm.formId];
390
- primary = observationFirstForm[form.primaryField];
391
- variant = observationFirstForm[form.variantField];
392
- }
393
- const properties = {
394
- lastModified: observation.lastModified,
395
- timestamp: observation.properties.timestamp,
396
- mageId: observation._id.toString(),
397
- createdAt: observation.createdAt,
398
- primaryField: primary,
399
- variantField: variant
400
- };
401
- if (observation.userId) {
402
- properties.userId = observation.userId.toString();
403
- }
404
- if (observation.deviceId) {
405
- properties.deviceId = observation.deviceId.toString();
364
+ }
365
+ addFormDataToGeoPackage(geopackage) {
366
+ return __awaiter(this, void 0, void 0, function* () {
367
+ const columns = [];
368
+ columns.push({
369
+ name: 'formName',
370
+ dataType: 'TEXT'
371
+ });
372
+ columns.push({
373
+ name: 'primaryField',
374
+ dataType: 'TEXT'
375
+ });
376
+ columns.push({
377
+ name: 'variantField',
378
+ dataType: 'TEXT'
379
+ });
380
+ columns.push({
381
+ name: 'color',
382
+ dataType: 'TEXT'
383
+ });
384
+ columns.push({
385
+ name: 'formId',
386
+ dataType: 'TEXT'
387
+ });
388
+ yield geopackage.createAttributesTableFromProperties('Forms', columns);
389
+ for (const form of this._event.forms) {
390
+ const row = {
391
+ formName: form.name,
392
+ primaryField: form.primaryField || null,
393
+ variantField: form.variantField || null,
394
+ color: form.color,
395
+ formId: form.id
396
+ };
397
+ geopackage.addAttributeRow('Forms', row);
406
398
  }
407
- const geojson = {
408
- type: 'Feature',
409
- geometry: observation.geometry,
410
- properties: properties
411
- };
412
- zoomToEnvelope = this.calculateBounds(geojson.geometry, zoomToEnvelope);
413
- const featureId = geopackage.addGeoJSONFeatureToGeoPackage(geojson, 'Observations');
414
- if (observation.properties.forms && observation.properties.forms[0]) {
415
- // insert the icon link
416
- let iconId = this.iconMap[observation.properties.forms[0].formId]['icon.png'];
417
- if (primary && this.iconMap[observation.properties.forms[0].formId][primary]) {
418
- iconId = this.iconMap[observation.properties.forms[0].formId][primary]['icon.png'];
399
+ });
400
+ }
401
+ addObservationsToGeoPackage(geopackage) {
402
+ return __awaiter(this, void 0, void 0, function* () {
403
+ log.info('requesting locations from db');
404
+ this.createAttachmentTable(geopackage);
405
+ const cursor = this.requestObservations(this._filter);
406
+ let numObservations = 0;
407
+ let zoomToEnvelope;
408
+ return cursor.eachAsync((observation) => __awaiter(this, void 0, void 0, function* () {
409
+ console.debug('exporting observation', observation.id, '...');
410
+ numObservations++;
411
+ if (!Array.isArray(observation.properties.forms)) {
412
+ return;
419
413
  }
420
- if (variant && this.iconMap[observation.properties.forms[0].formId][primary] && this.iconMap[observation.properties.forms[0].formId][primary][variant]) {
421
- iconId = this.iconMap[observation.properties.forms[0].formId][primary][variant];
414
+ const primaryEntry = observation.properties.forms[0];
415
+ if (!primaryEntry) {
416
+ return;
422
417
  }
423
- const featureTableStyles = new GeoPackageAPI.FeatureTableStyles(geopackage, 'Observations');
424
- featureTableStyles.setIconDefault(featureId, iconId);
425
- }
426
- if (observation.properties.forms) {
427
- for (let f = 0; f < observation.properties.forms.length; f++) {
428
- const observationForm = observation.properties.forms[f];
429
- const formDefinition = this._event.formMap[observationForm.formId];
430
- primary = observationForm[formDefinition.primaryField];
431
- variant = observationForm[formDefinition.variantField];
418
+ const form = this._event.formFor(primaryEntry.formId);
419
+ const primary = (form === null || form === void 0 ? void 0 : form.primaryField) ? String(primaryEntry[form.primaryField]) : null;
420
+ const variant = (form === null || form === void 0 ? void 0 : form.primaryField) && (form === null || form === void 0 ? void 0 : form.variantField) ? String(primaryEntry[form.variantField]) : null;
421
+ const properties = {
422
+ lastModified: observation.lastModified,
423
+ timestamp: observation.properties.timestamp,
424
+ mageId: observation._id.toString(),
425
+ createdAt: observation.createdAt,
426
+ primaryField: primary,
427
+ variantField: variant
428
+ };
429
+ if (observation.userId) {
430
+ properties.userId = observation.userId.toString();
431
+ }
432
+ if (observation.deviceId) {
433
+ properties.deviceId = observation.deviceId.toString();
434
+ }
435
+ const feature = {
436
+ type: 'Feature',
437
+ geometry: observation.geometry,
438
+ properties
439
+ };
440
+ zoomToEnvelope = calculateBounds(feature.geometry, zoomToEnvelope);
441
+ const featureId = geopackage.addGeoJSONFeatureToGeoPackage(feature, 'Observations');
442
+ const iconSpec = {
443
+ eventId: this._event.id,
444
+ formId: primaryEntry.formId,
445
+ primary,
446
+ variant,
447
+ };
448
+ yield this.linkObservationFeatureIcon(iconSpec, featureId);
449
+ const formEntries = observation.properties.forms || [];
450
+ for (const formEntry of formEntries) {
451
+ const form = this._event.formFor(formEntry.formId);
452
+ const primary = form.primaryField ? String(formEntry[form.primaryField]) : null;
453
+ const variant = form.primaryField && form.variantField ? String(formEntry[form.variantField]) : null;
432
454
  const formToSave = {
433
455
  primaryField: primary,
434
456
  variantField: variant,
435
- formId: observationForm.formId
457
+ formId: formEntry.formId
436
458
  };
437
459
  const attachments = [];
438
460
  if (observation.attachments) {
439
461
  observation.attachments.forEach((attachment) => {
440
- if (attachment.observationFormId.toString() == observationForm._id) {
462
+ if (String(attachment.observationFormId) === String(formEntry._id)) {
441
463
  attachments.push(attachment);
442
- observationForm[attachment.fieldName] = observationForm[attachment.fieldName] || [];
443
- observationForm[attachment.fieldName].push(attachment._id.toString());
464
+ const attachmentFieldEntries = (formEntry[attachment.fieldName] || []);
465
+ attachmentFieldEntries.push(String(attachment._id));
466
+ formEntry[attachment.fieldName] = attachmentFieldEntries;
444
467
  }
445
468
  });
446
469
  }
447
- Object.keys(observationForm).forEach(key => {
448
- if (observationForm[key] == null)
470
+ Object.keys(formEntry).forEach(key => {
471
+ const fieldEntry = formEntry[key];
472
+ if (fieldEntry === null || fieldEntry === undefined) {
449
473
  return;
450
- const fieldDefinition = formDefinition.fields.find(field => field.name === key);
451
- if (!fieldDefinition)
474
+ }
475
+ const field = this._event.formFieldFor(key, form.id);
476
+ if (!field) {
452
477
  return;
453
- if (fieldDefinition.type === 'multiselectdropdown') {
454
- formToSave[key] = observationForm[key].join(', ');
455
478
  }
456
- else if (fieldDefinition.type === 'date') {
457
- formToSave[key] = moment(observationForm[key]).toISOString();
479
+ if (field.type === 'multiselectdropdown') {
480
+ formToSave[key] = fieldEntry.join(', ');
458
481
  }
459
- else if (fieldDefinition.type === 'checkbox') {
460
- formToSave[key] = observationForm[key].toString();
482
+ else if (field.type === 'date') {
483
+ formToSave[key] = (0, moment_1.default)(fieldEntry).toISOString();
461
484
  }
462
- else if (fieldDefinition.type === 'geometry') {
463
- formToSave[key] = wkx.Geometry.parseGeoJSON(observationForm[key]).toWkt();
485
+ else if (field.type === 'checkbox') {
486
+ formToSave[key] = String(fieldEntry);
464
487
  }
465
- else if (fieldDefinition.type === 'attachment') {
466
- formToSave[key] = observationForm[key].join(', ');
488
+ else if (field.type === 'geometry') {
489
+ formToSave[key] = wkx_1.default.Geometry.parseGeoJSON(fieldEntry).toWkt();
490
+ }
491
+ else if (field.type === 'attachment') {
492
+ formToSave[key] = fieldEntry.join(', ');
467
493
  }
468
494
  else {
469
- formToSave[key] = observationForm[key];
495
+ formToSave[key] = fieldEntry;
470
496
  }
471
497
  });
472
498
  try {
473
499
  const rowId = geopackage.addAttributeRow('Form_' + formToSave.formId, formToSave);
474
500
  if (attachments.length) {
475
- yield this.addAttachments(geopackage, attachments, featureId, 'Form_' + formToSave.formId, rowId);
501
+ yield addAttachments(geopackage, attachments, featureId, 'Form_' + formToSave.formId, rowId);
476
502
  }
477
- yield geopackage.linkRelatedRows('Observations', featureId, 'Form_' + formToSave.formId, rowId, RelationType.ATTRIBUTES);
503
+ yield geopackage.linkRelatedRows('Observations', featureId, 'Form_' + formToSave.formId, rowId, relationType_1.RelationType.ATTRIBUTES);
478
504
  }
479
505
  catch (e) {
480
- console.error('error is ', e);
506
+ log.error(`error writing rows for form entry ${formEntry.id} of observation ${observation.id} to geopackage`, e);
481
507
  }
482
508
  }
509
+ }))
510
+ .then(() => __awaiter(this, void 0, void 0, function* () {
511
+ if (cursor) {
512
+ yield cursor.close();
513
+ }
514
+ const featureDao = geopackage.getFeatureDao('Observations');
515
+ const rtreeIndex = new GPKG.RTreeIndex(geopackage, featureDao);
516
+ rtreeIndex.create();
517
+ if (zoomToEnvelope) {
518
+ setContentBounds(geopackage, featureDao, zoomToEnvelope);
519
+ }
520
+ log.info(`'wrote ${numObservations} observations to geopackage`);
521
+ }))
522
+ .catch(err => {
523
+ log.warn(err);
524
+ });
525
+ });
526
+ }
527
+ createObservationFeatureTableStyles(geopackage) {
528
+ return __awaiter(this, void 0, void 0, function* () {
529
+ const featureTableName = 'Observations';
530
+ const featureTableStyles = new GPKG.FeatureTableStyles(geopackage, featureTableName);
531
+ yield geopackage.featureStyleExtension.getOrCreateExtension(featureTableName);
532
+ yield geopackage.featureStyleExtension.getRelatedTables().getOrCreateExtension();
533
+ yield geopackage.featureStyleExtension.getContentsId().getOrCreateExtension();
534
+ featureTableStyles.createRelationships();
535
+ const defaultIconAccess = new api_1.default.Icon(this._event.id);
536
+ const defaultIconDoc = yield util_1.default.promisify(defaultIconAccess.getIcon.bind(defaultIconAccess))();
537
+ if (!defaultIconDoc || isNothing(defaultIconDoc.path)) {
538
+ return featureTableStyles;
483
539
  }
484
- })).then(() => __awaiter(this, void 0, void 0, function* () {
485
- if (cursor)
486
- cursor.close;
487
- const featureDao = geopackage.getFeatureDao('Observations');
488
- const rtreeIndex = new GeoPackageAPI.RTreeIndex(geopackage, featureDao);
489
- rtreeIndex.create();
490
- if (zoomToEnvelope) {
491
- this.setContentBounds(geopackage, featureDao, zoomToEnvelope);
540
+ try {
541
+ const iconBytes = yield promises_1.default.readFile(defaultIconDoc.path);
542
+ const gpkgIconRow = featureTableStyles.getIconDao().newRow();
543
+ gpkgIconRow.data = iconBytes;
544
+ populateGpkgIconRow(gpkgIconRow, defaultIconDoc, this._event);
545
+ featureTableStyles.setTableIconDefault(gpkgIconRow);
546
+ this.iconCache.put(defaultIconDoc, gpkgIconRow.id);
492
547
  }
493
- log.info('Successfully wrote ' + numObservations + ' observations to Geopackage');
494
- return geopackage;
495
- })).catch(err => { log.warn(err); });
496
- });
497
- };
498
- GeoPackage.prototype.calculateBounds = function (geometry, zoomToEnvelope) {
499
- const wkxGeometry = wkx.Geometry.parseGeoJSON(geometry);
500
- const envelope = EnvelopeBuilder.buildEnvelopeWithGeometry(wkxGeometry);
501
- if (!zoomToEnvelope) {
502
- zoomToEnvelope = envelope;
548
+ catch (err) {
549
+ console.warn('error setting default icon', defaultIconDoc.path);
550
+ }
551
+ return featureTableStyles;
552
+ });
553
+ }
554
+ linkObservationFeatureIcon(iconSpec, featureId) {
555
+ return __awaiter(this, void 0, void 0, function* () {
556
+ const iconId = yield this.ensureIconInGeopackage(iconSpec);
557
+ if (iconId === null) {
558
+ return;
559
+ }
560
+ const styleExt = this.observationStyles.getFeatureStyleExtension();
561
+ const iconMappingDao = this.observationStyles.getIconMappingDao();
562
+ styleExt.insertStyleMapping(iconMappingDao, featureId, iconId);
563
+ });
503
564
  }
504
- else {
505
- if (zoomToEnvelope.maxX < envelope.maxX) {
506
- zoomToEnvelope.maxX = envelope.maxX;
565
+ ensureIconInGeopackage(iconSpec) {
566
+ return __awaiter(this, void 0, void 0, function* () {
567
+ const cachedIconId = this.iconCache.get(iconSpec);
568
+ if (cachedIconId === IconTreeCache.ICON_LOAD_ERROR) {
569
+ return null;
570
+ }
571
+ if (cachedIconId !== null) {
572
+ return cachedIconId;
573
+ }
574
+ const iconAccess = new api_1.default.Icon(this._event.id, iconSpec.formId, iconSpec.primary, iconSpec.variant);
575
+ const iconDoc = yield util_1.default.promisify(iconAccess.getIcon.bind(iconAccess))();
576
+ if (!iconDoc || isNothing(iconDoc.path)) {
577
+ return null;
578
+ }
579
+ try {
580
+ const iconBytes = yield promises_1.default.readFile(iconDoc.path);
581
+ const gpkgIconRow = this.observationStyles.getIconDao().newRow();
582
+ gpkgIconRow.data = iconBytes;
583
+ populateGpkgIconRow(gpkgIconRow, iconDoc, this._event);
584
+ const id = this.observationStyles.getIconDao().create(gpkgIconRow);
585
+ this.iconCache.put(iconDoc, id);
586
+ return id;
587
+ }
588
+ catch (err) {
589
+ console.warn('error adding icon', iconDoc.path, err);
590
+ }
591
+ this.iconCache.put(iconDoc, IconTreeCache.ICON_LOAD_ERROR);
592
+ return null;
593
+ });
594
+ }
595
+ }
596
+ exports.GeoPackage = GeoPackage;
597
+ function populateGpkgIconRow(gpkgIconRow, iconDoc, mageEvent) {
598
+ gpkgIconRow.contentType = 'image/png';
599
+ gpkgIconRow.width = 20;
600
+ gpkgIconRow.anchorU = 0.5;
601
+ gpkgIconRow.anchorV = 1.0;
602
+ const defaultName = `${mageEvent.name} default`;
603
+ const name = (() => {
604
+ if (isNothing(iconDoc.formId)) {
605
+ if (isNothing(iconDoc.primary)) {
606
+ if (isNothing(iconDoc.variant)) {
607
+ return defaultName;
608
+ }
609
+ return iconDoc.variant;
610
+ }
611
+ return iconDoc.primary;
507
612
  }
508
- if (zoomToEnvelope.maxY < envelope.maxY) {
509
- zoomToEnvelope.maxY = envelope.maxY;
613
+ const form = mageEvent.formFor(iconDoc.formId);
614
+ if (!form) {
615
+ return defaultName;
510
616
  }
511
- if (zoomToEnvelope.minX > envelope.minX) {
512
- zoomToEnvelope.minX = envelope.minX;
617
+ return `${form.name} icon`;
618
+ })();
619
+ gpkgIconRow.name = name;
620
+ return gpkgIconRow;
621
+ }
622
+ function isNothing(wut) {
623
+ return wut === null || wut === undefined || wut === '' || (typeof wut === 'number' && isNaN(wut));
624
+ }
625
+ class IconTreeCache {
626
+ constructor() {
627
+ this.root = new IconTreeCacheNode();
628
+ }
629
+ get(icon) {
630
+ const { formId, primary, variant } = icon;
631
+ if (!isNothing(formId)) {
632
+ const formNode = this.root.children[formId];
633
+ if (formNode) {
634
+ if (!isNothing(primary)) {
635
+ const primaryNode = formNode.children[primary];
636
+ if (primaryNode) {
637
+ if (!isNothing(variant)) {
638
+ const variantNode = primaryNode.children[variant];
639
+ return (variantNode === null || variantNode === void 0 ? void 0 : variantNode.gpkgIconId) || null;
640
+ }
641
+ return primaryNode.gpkgIconId;
642
+ }
643
+ return null;
644
+ }
645
+ return formNode.gpkgIconId;
646
+ }
647
+ return null;
513
648
  }
514
- if (zoomToEnvelope.minY > envelope.minY) {
515
- zoomToEnvelope.minY = envelope.minY;
649
+ return this.root.gpkgIconId;
650
+ }
651
+ put(icon, gpkgIconId) {
652
+ const node = this.ensurePathNodes(icon);
653
+ node.gpkgIconId = gpkgIconId;
654
+ return this;
655
+ }
656
+ ensurePathNodes(path) {
657
+ const { formId, primary, variant } = path;
658
+ if (!isNothing(formId)) {
659
+ const formNode = this.root.children[formId] = this.root.children[formId] || new IconTreeCacheNode();
660
+ if (!isNothing(primary)) {
661
+ const primaryNode = formNode.children[primary] = formNode.children[primary] || new IconTreeCacheNode();
662
+ if (!isNothing(variant)) {
663
+ return primaryNode.children[variant] = primaryNode.children[variant] || new IconTreeCacheNode();
664
+ }
665
+ return primaryNode;
666
+ }
667
+ return formNode;
516
668
  }
669
+ return this.root;
670
+ }
671
+ }
672
+ IconTreeCache.ICON_LOAD_ERROR = Number.MIN_SAFE_INTEGER;
673
+ class IconTreeCacheNode {
674
+ constructor(gpkgRowId = null, children = {}) {
675
+ this.gpkgIconId = null;
676
+ this.gpkgIconId = isNothing(gpkgRowId) ? null : gpkgRowId;
677
+ this.children = children;
678
+ }
679
+ }
680
+ function createGeoPackageFile() {
681
+ const filename = (0, moment_1.default)().format('YYYMMDD_hhmmssSSS') + '.gpkg';
682
+ const filePath = path_1.default.join(os_1.default.tmpdir(), filename);
683
+ return new Promise(function (resolve, reject) {
684
+ fs_1.default.unlink(filePath, function () {
685
+ fs_1.default.mkdir(path_1.default.dirname(filePath), function () {
686
+ fs_1.default.open(filePath, 'w', function (err) {
687
+ if (err)
688
+ return reject(err);
689
+ resolve(filePath);
690
+ });
691
+ });
692
+ });
693
+ });
694
+ }
695
+ function calculateBounds(geometry, zoomToEnvelope) {
696
+ const wkxGeometry = wkx_1.default.Geometry.parseGeoJSON(geometry);
697
+ const envelope = envelopeBuilder_1.EnvelopeBuilder.buildEnvelopeWithGeometry(wkxGeometry);
698
+ if (!zoomToEnvelope) {
699
+ return envelope;
700
+ }
701
+ if (zoomToEnvelope.maxX < envelope.maxX) {
702
+ zoomToEnvelope.maxX = envelope.maxX;
703
+ }
704
+ if (zoomToEnvelope.maxY < envelope.maxY) {
705
+ zoomToEnvelope.maxY = envelope.maxY;
706
+ }
707
+ if (zoomToEnvelope.minX > envelope.minX) {
708
+ zoomToEnvelope.minX = envelope.minX;
709
+ }
710
+ if (zoomToEnvelope.minY > envelope.minY) {
711
+ zoomToEnvelope.minY = envelope.minY;
517
712
  }
518
713
  return zoomToEnvelope;
519
- };
520
- GeoPackage.prototype.addAttachments = function (geopackage, attachments, observationId, formTable, formRowId) {
714
+ }
715
+ function addAttachments(geopackage, attachments, observationId, formTable, formRowId) {
521
716
  return __awaiter(this, void 0, void 0, function* () {
522
- log.info('Add Attachments');
717
+ log.info('add attachments');
523
718
  for (let i = 0; i < attachments.length; i++) {
524
719
  const attachment = attachments[i];
525
720
  if (attachment.relativePath) {
526
721
  yield new Promise(function (resolve, reject) {
527
- fs.readFile(path.join(attachmentBase, attachment.relativePath), (err, dataBuffer) => __awaiter(this, void 0, void 0, function* () {
528
- if (err)
722
+ fs_1.default.readFile(path_1.default.join(attachmentBase, attachment.relativePath), (err, dataBuffer) => __awaiter(this, void 0, void 0, function* () {
723
+ if (err) {
529
724
  return reject(err);
530
- const mediaId = geopackage.addMedia('Attachments', dataBuffer, attachment.contentType, {
531
- name: attachment.name,
532
- size: attachment.size
725
+ }
726
+ const mediaId = geopackage.addMedia('Attachments', dataBuffer, attachment.contentType || 'application/octet-stream', {
727
+ name: attachment.name || attachment._id,
728
+ size: attachment.size || 0
533
729
  });
534
730
  yield geopackage.linkMedia('Observations', observationId, 'Attachments', mediaId);
535
731
  resolve(geopackage.linkMedia(formTable, formRowId, 'Attachments', mediaId));
@@ -538,144 +734,18 @@ GeoPackage.prototype.addAttachments = function (geopackage, attachments, observa
538
734
  }
539
735
  }
540
736
  });
541
- };
542
- GeoPackage.prototype.createObservationFeatureTableStyles = function (geopackage) {
543
- return __awaiter(this, void 0, void 0, function* () {
544
- const featureTableName = 'Observations';
545
- const featureTableStyles = new GeoPackageAPI.FeatureTableStyles(geopackage, featureTableName);
546
- yield geopackage.featureStyleExtension.getOrCreateExtension(featureTableName);
547
- yield geopackage.featureStyleExtension.getRelatedTables().getOrCreateExtension();
548
- yield geopackage.featureStyleExtension.getContentsId().getOrCreateExtension();
549
- featureTableStyles.createRelationships();
550
- yield this.addObservationIcons(geopackage, featureTableStyles);
551
- return geopackage;
552
- });
553
- };
554
- GeoPackage.prototype.createUserFeatureTableStyles = function (geopackage) {
737
+ }
738
+ function createUserFeatureTableStyles(geopackage) {
555
739
  return __awaiter(this, void 0, void 0, function* () {
556
740
  const featureTableName = 'Users';
557
- const featureTableStyles = new GeoPackageAPI.FeatureTableStyles(geopackage, featureTableName);
741
+ const featureTableStyles = new GPKG.FeatureTableStyles(geopackage, featureTableName);
558
742
  yield geopackage.featureStyleExtension.getOrCreateExtension(featureTableName);
559
743
  yield geopackage.featureStyleExtension.getRelatedTables().getOrCreateExtension();
560
744
  yield geopackage.featureStyleExtension.getContentsId().getOrCreateExtension();
561
745
  featureTableStyles.createRelationships();
562
- return geopackage;
563
746
  });
564
- };
565
- GeoPackage.prototype.addObservationIcons = function (geopackage, featureTableStyles) {
566
- return __awaiter(this, void 0, void 0, function* () {
567
- const rootDir = path.join(new api.Icon(this._event._id).getBasePath());
568
- if (!fs.existsSync(path.join(rootDir))) {
569
- return geopackage;
570
- }
571
- const formDirs = fs.readdirSync(path.join(rootDir));
572
- for (let i = 0; i < formDirs.length; i++) {
573
- const formDir = formDirs[i];
574
- this.iconMap[formDir] = this.iconMap[formDir] || {};
575
- if (!fs.existsSync(path.join(rootDir, formDir))) {
576
- continue;
577
- }
578
- if (formDir === 'icon.png') {
579
- yield new Promise((resolve, reject) => {
580
- fs.readFile(path.join(rootDir, formDir), (err, iconBuffer) => __awaiter(this, void 0, void 0, function* () {
581
- if (err)
582
- return reject(err);
583
- const iconRow = featureTableStyles.getIconDao().newRow();
584
- iconRow.data = iconBuffer;
585
- iconRow.contentType = 'image/png';
586
- iconRow.name = `${this._event.name} icon`;
587
- iconRow.description = `Icon for event ${this._event.name}`;
588
- iconRow.width = 20;
589
- iconRow.anchorU = 0.5;
590
- iconRow.anchorV = 1.0;
591
- this.iconMap[formDir] = iconRow;
592
- yield featureTableStyles.setTableIconDefault(iconRow);
593
- resolve();
594
- }));
595
- });
596
- }
597
- else {
598
- const primaryDirs = fs.readdirSync(path.join(rootDir, formDir));
599
- for (let p = 0; p < primaryDirs.length; p++) {
600
- const primaryDir = primaryDirs[p];
601
- if (!fs.existsSync(path.join(rootDir, formDir, primaryDir))) {
602
- continue;
603
- }
604
- if (primaryDir === 'icon.png') {
605
- yield new Promise((resolve, reject) => {
606
- fs.readFile(path.join(rootDir, formDir, primaryDir), (err, iconBuffer) => {
607
- if (err)
608
- return reject(err);
609
- const iconRow = featureTableStyles.getIconDao().newRow();
610
- iconRow.data = iconBuffer;
611
- iconRow.contentType = 'image/png';
612
- iconRow.name = formDir;
613
- iconRow.description = `Icon for form ${formDir}/icon.png`;
614
- iconRow.width = 20;
615
- iconRow.anchorU = 0.5;
616
- iconRow.anchorV = 1.0;
617
- this.iconMap[formDir]['icon.png'] = iconRow;
618
- resolve();
619
- });
620
- });
621
- }
622
- else {
623
- this.iconMap[formDir][primaryDir] = this.iconMap[formDir][primaryDir] || {};
624
- const variantDirs = fs.readdirSync(path.join(rootDir, formDir, primaryDir));
625
- for (let v = 0; v < variantDirs.length; v++) {
626
- const variantDir = variantDirs[v];
627
- if (!fs.existsSync(path.join(rootDir, formDir, primaryDir, variantDir))) {
628
- continue;
629
- }
630
- if (variantDir === 'icon.png') {
631
- yield new Promise((resolve, reject) => {
632
- fs.readFile(path.join(rootDir, formDir, primaryDir, variantDir), (err, iconBuffer) => {
633
- if (err)
634
- return reject(err);
635
- const iconRow = featureTableStyles.getIconDao().newRow();
636
- iconRow.data = iconBuffer;
637
- iconRow.contentType = 'image/png';
638
- iconRow.name = primaryDir;
639
- iconRow.description = `Icon for form ${formDir}/${primaryDir}/icon.png`;
640
- iconRow.width = 20;
641
- iconRow.anchorU = 0.5;
642
- iconRow.anchorV = 1.0;
643
- this.iconMap[formDir][primaryDir]['icon.png'] = iconRow;
644
- resolve();
645
- });
646
- });
647
- }
648
- else {
649
- this.iconMap[formDir][primaryDir][variantDir] = this.iconMap[formDir][primaryDir][variantDir] || {};
650
- if (!fs.existsSync(path.join(rootDir, formDir, primaryDir, variantDir, 'icon.png'))) {
651
- continue;
652
- }
653
- yield new Promise((resolve, reject) => {
654
- fs.readFile(path.join(rootDir, formDir, primaryDir, variantDir, 'icon.png'), (err, iconBuffer) => {
655
- if (err)
656
- return reject(err);
657
- const iconRow = featureTableStyles.getIconDao().newRow();
658
- iconRow.data = iconBuffer;
659
- iconRow.contentType = 'image/png';
660
- iconRow.name = variantDir;
661
- iconRow.description = `Icon for form ${formDir}/${primaryDir}/${variantDir}/icon.png`;
662
- iconRow.width = 20;
663
- iconRow.anchorU = 0.5;
664
- iconRow.anchorV = 1.0;
665
- this.iconMap[formDir][primaryDir][variantDir]['icon.png'] = iconRow;
666
- resolve();
667
- });
668
- });
669
- }
670
- }
671
- }
672
- }
673
- }
674
- }
675
- return geopackage;
676
- });
677
- };
678
- GeoPackage.prototype.setContentBounds = function (geopackage, featureDao, zoomToEnvelope) {
747
+ }
748
+ function setContentBounds(geopackage, featureDao, zoomToEnvelope) {
679
749
  const contents = featureDao.getContents();
680
750
  contents.max_x = zoomToEnvelope.maxX;
681
751
  contents.max_y = zoomToEnvelope.maxY;
@@ -683,5 +753,5 @@ GeoPackage.prototype.setContentBounds = function (geopackage, featureDao, zoomTo
683
753
  contents.min_y = zoomToEnvelope.minY;
684
754
  const contentsDao = geopackage.contentsDao;
685
755
  contentsDao.update(contents);
686
- };
756
+ }
687
757
  //# sourceMappingURL=geopackage.js.map