@ngageoint/mage.service 6.2.8 → 6.2.10-beta.1

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