@esri/solutions-components 0.4.3 → 0.4.5

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 (30) hide show
  1. package/dist/assets/t9n/public-notification/resources.json +5 -5
  2. package/dist/assets/t9n/public-notification/resources_en.json +5 -5
  3. package/dist/cjs/calcite-input-message_5.cjs.entry.js +1 -17
  4. package/dist/cjs/{downloadUtils-ae182e3a.js → downloadUtils-d05b069f.js} +91 -42
  5. package/dist/cjs/{index.es-b71e9071.js → index.es-e47b660c.js} +1 -1
  6. package/dist/cjs/layer-table.cjs.entry.js +1 -1
  7. package/dist/cjs/public-notification.cjs.entry.js +3 -11
  8. package/dist/collection/components/map-select-tools/map-select-tools.js +0 -16
  9. package/dist/collection/components/public-notification/public-notification.js +3 -11
  10. package/dist/collection/utils/downloadUtils.js +90 -41
  11. package/dist/collection/utils/downloadUtils.ts +123 -52
  12. package/dist/components/downloadUtils.js +90 -41
  13. package/dist/components/map-select-tools2.js +0 -16
  14. package/dist/components/public-notification.js +3 -11
  15. package/dist/esm/calcite-input-message_5.entry.js +1 -17
  16. package/dist/esm/{downloadUtils-8d64719e.js → downloadUtils-a9b39194.js} +91 -42
  17. package/dist/esm/{index.es-1d777745.js → index.es-4904465f.js} +1 -1
  18. package/dist/esm/layer-table.entry.js +1 -1
  19. package/dist/esm/public-notification.entry.js +3 -11
  20. package/dist/solutions-components/p-20c73673.entry.js +6 -0
  21. package/dist/solutions-components/{p-702e1915.js → p-25eb36dc.js} +1 -1
  22. package/dist/solutions-components/p-59caff67.entry.js +17 -0
  23. package/dist/solutions-components/{p-048a4c91.entry.js → p-a606221a.entry.js} +1 -1
  24. package/dist/solutions-components/{p-01215468.js → p-e7b3a4d9.js} +3 -3
  25. package/dist/solutions-components/solutions-components.esm.js +1 -1
  26. package/dist/solutions-components/utils/downloadUtils.ts +123 -52
  27. package/dist/types/components/json-editor/assets/monaco-editor/monaco.d.ts +8262 -0
  28. package/package.json +1 -1
  29. package/dist/solutions-components/p-4bfcd9ea.entry.js +0 -17
  30. package/dist/solutions-components/p-a99ca396.entry.js +0 -6
@@ -24,6 +24,7 @@ import { exportPDF } from "./pdfUtils";
24
24
  import { loadModules } from "./loadModules";
25
25
  import { queryFeaturesByID } from "./queryUtils";
26
26
  export { ILabel } from "./pdfUtils";
27
+ const lineSeparatorChar = "|";
27
28
  //#endregion
28
29
  //#region Public functions
29
30
  /**
@@ -56,7 +57,12 @@ export async function downloadCSV(selectionSetNames, layer, ids, formatUsingLaye
56
57
  */
57
58
  export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicates, labelPageDescription) {
58
59
  console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
59
- const labels = await _prepareLabels(layer, ids, removeDuplicates);
60
+ let labels = await _prepareLabels(layer, ids, removeDuplicates);
61
+ labels =
62
+ // Remove empty lines in labels
63
+ labels.map(labelLines => labelLines.filter(line => line.length > 0))
64
+ // Remove empty labels
65
+ .filter(label => label.length > 0);
60
66
  exportPDF(labels, labelPageDescription);
61
67
  return Promise.resolve();
62
68
  }
@@ -67,7 +73,7 @@ export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicate
67
73
  *
68
74
  * @param fieldInfos Layer's fieldInfos structure
69
75
  * @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
70
- * @return Label spec
76
+ * @return Label spec with lines separated by `lineSeparatorChar`
71
77
  */
72
78
  function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false) {
73
79
  const labelSpec = [];
@@ -77,7 +83,7 @@ function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false
77
83
  labelSpec.push(`{${fieldInfo.fieldName}}`);
78
84
  }
79
85
  });
80
- return labelSpec;
86
+ return labelSpec.join(lineSeparatorChar);
81
87
  }
82
88
  ;
83
89
  /**
@@ -86,22 +92,19 @@ function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false
86
92
  *
87
93
  * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
88
94
  * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
89
- * @return Label spec
95
+ * @return Label spec with lines separated by `lineSeparatorChar`
90
96
  */
91
97
  function _convertPopupTextToLabelSpec(popupInfo) {
92
- // Replace <br>, <br/> with |
93
- popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
94
- // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
98
+ // Replace <br>, <br/> with the line separator character
99
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, lineSeparatorChar);
100
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, and replace some char representations,
95
101
  // and split the label back into individual lines
96
- let labelSpec = popupInfo
102
+ const labelSpec = popupInfo
97
103
  .replace(/<[\s.]*[^<>]*\/?>/gi, "")
98
104
  .replace(/\xA0/gi, " ")
99
105
  .replace(/&lt;/gi, "<")
100
106
  .replace(/&gt;/gi, ">")
101
- .replace(/&nbsp;/gi, " ")
102
- .split("|");
103
- // Trim lines and remove empties
104
- labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
107
+ .replace(/&nbsp;/gi, " ");
105
108
  return labelSpec;
106
109
  }
107
110
  ;
@@ -121,7 +124,7 @@ async function _createArcadeExecutors(labelFormat, layer) {
121
124
  }
122
125
  // Are there any Arcade expressions in the label format?
123
126
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
124
- const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
127
+ const arcadeExpressionsMatches = labelFormat.match(arcadeExpressionRegExp);
125
128
  if (!arcadeExpressionsMatches) {
126
129
  return Promise.resolve(arcadeExecutors);
127
130
  }
@@ -148,6 +151,39 @@ async function _createArcadeExecutors(labelFormat, layer) {
148
151
  return arcadeExecutors;
149
152
  });
150
153
  }
154
+ /**
155
+ * Prepares an attribute's value by applying domain and type information.
156
+ *
157
+ * @param attributeValue Value of attribute
158
+ * @param attributeType Type of attribute
159
+ * @param attributeDomain Domain info for attribute, if any
160
+ * @return Attribute value modified appropriate to domain and type
161
+ */
162
+ function _prepareAttributeValue(attributeValue, attributeType, attributeDomain, intl) {
163
+ if (attributeDomain && attributeDomain.type === "coded-value") {
164
+ // "coded-value" domain field
165
+ const value = attributeDomain.getName(attributeValue);
166
+ return value;
167
+ }
168
+ else {
169
+ // Non-domain field or unsupported domain type
170
+ let value = attributeValue;
171
+ switch (attributeType) {
172
+ case "date":
173
+ // Format date produces odd characters for the space between the time and the AM/PM text,
174
+ // e.g., "12/31/1969, 4:00 PM"
175
+ value = intl.formatDate(value).replace(/\xe2\x80\xaf/g, "");
176
+ break;
177
+ case "double":
178
+ case "integer":
179
+ case "long":
180
+ case "small-integer":
181
+ value = intl.formatNumber(value);
182
+ break;
183
+ }
184
+ return value;
185
+ }
186
+ }
151
187
  /**
152
188
  * Creates labels from items.
153
189
  *
@@ -164,6 +200,13 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
164
200
  const [intl] = await loadModules(["esri/intl"]);
165
201
  // Get the features to export
166
202
  const featureSet = await queryFeaturesByID(ids, layer);
203
+ // Get field data types. Do we have any domain-based fields?
204
+ const attributeTypes = {};
205
+ const attributeDomains = {};
206
+ layer.fields.forEach(field => {
207
+ attributeTypes[field.name] = field.type;
208
+ attributeDomains[field.name] = field.domain;
209
+ });
167
210
  // Get the label formatting, if any
168
211
  let labelFormat;
169
212
  let arcadeExecutors = {};
@@ -177,7 +220,7 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
177
220
  // Can we use the popup title?
178
221
  // eslint-disable-next-line unicorn/prefer-ternary
179
222
  if (typeof layer.popupTemplate.title === "string") {
180
- labelFormat = [layer.popupTemplate.title];
223
+ labelFormat = layer.popupTemplate.title;
181
224
  // Otherwise revert to using attributes
182
225
  }
183
226
  else {
@@ -196,35 +239,43 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
196
239
  // eslint-disable-next-line unicorn/prefer-ternary
197
240
  if (labelFormat) {
198
241
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
199
- // Convert attributes into an array of labels
242
+ const attributeRegExp = /\{\w+\}/g;
243
+ // Find the label fields that we need to replace with values
244
+ const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
245
+ const attributeMatches = labelFormat.match(attributeRegExp);
246
+ // Convert feature attributes into an array of labels
200
247
  labels = featureSet.features.map(feature => {
201
- const label = [];
202
- labelFormat.forEach(labelLineTemplate => {
203
- let labelLine = labelLineTemplate;
204
- // Replace Arcade expressions
205
- const arcadeExpressionsMatches = labelLine.match(arcadeExpressionRegExp);
206
- if (arcadeExpressionsMatches) {
207
- arcadeExpressionsMatches.forEach((match) => {
208
- const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
209
- const replacement = arcadeExecutors[expressionName].execute({ "$feature": feature });
210
- labelLine = labelLine.replace(match, replacement);
211
- });
212
- }
213
- // Replace fields; must be done after Arcade check because `substitute` will discard Arcade expressions!
214
- labelLine = intl.substitute(labelLine, feature.attributes).trim();
215
- if (labelLine.length > 0) {
216
- label.push(labelLine);
217
- }
218
- });
248
+ let labelPrep = labelFormat;
249
+ // Replace Arcade expressions
250
+ if (arcadeExpressionMatches) {
251
+ arcadeExpressionMatches.forEach((match) => {
252
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
253
+ const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
254
+ labelPrep = labelPrep.replace(match, value);
255
+ });
256
+ }
257
+ // Replace non-Arcade fields
258
+ if (attributeMatches) {
259
+ attributeMatches.forEach((match) => {
260
+ const attributeName = match.substring(1, match.length - 1);
261
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], intl);
262
+ labelPrep = labelPrep.replace(match, value);
263
+ });
264
+ }
265
+ // Split label into lines
266
+ let label = labelPrep.split(lineSeparatorChar);
267
+ // Trim lines
268
+ label = label.map(line => line.trim());
219
269
  return label;
220
- })
221
- // Remove empty labels
222
- .filter(label => label.length > 0);
270
+ });
223
271
  }
224
272
  else {
225
273
  // Export all attributes
226
274
  labels = featureSet.features.map(feature => {
227
- return Object.values(feature.attributes).map(attribute => `${attribute}`);
275
+ return Object.keys(feature.attributes).map((attributeName) => {
276
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], intl);
277
+ return `${value}`;
278
+ });
228
279
  });
229
280
  }
230
281
  // Remove duplicates
@@ -237,14 +288,12 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
237
288
  if (includeHeaderNames) {
238
289
  let headerNames = [];
239
290
  if (labelFormat) {
240
- headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
291
+ headerNames = labelFormat.replace(/\{/g, "").replace(/\}/g, "").split(lineSeparatorChar);
241
292
  }
242
293
  else {
243
294
  const featuresAttrs = featureSet.features[0].attributes;
244
295
  Object.keys(featuresAttrs).forEach(k => {
245
- if (featuresAttrs[0].hasOwnProperty(k)) {
246
- headerNames.push(k);
247
- }
296
+ headerNames.push(k);
248
297
  });
249
298
  }
250
299
  labels.unshift(headerNames);
@@ -31,6 +31,16 @@ interface IArcadeExecutorPromises {
31
31
  [expressionName: string]: Promise<__esri.ArcadeExecutor>;
32
32
  }
33
33
 
34
+ interface IAttributeDomains {
35
+ [attributeName: string]: __esri.CodedValueDomain | __esri.RangeDomain | __esri.InheritedDomain | null;
36
+ }
37
+
38
+ interface IAttributeTypes {
39
+ [attributeName: string]: string;
40
+ }
41
+
42
+ const lineSeparatorChar = "|";
43
+
34
44
  //#endregion
35
45
  //#region Public functions
36
46
 
@@ -80,7 +90,13 @@ export async function downloadPDF(
80
90
  labelPageDescription: ILabel
81
91
  ): Promise<void> {
82
92
  console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames));//???
83
- const labels = await _prepareLabels(layer, ids, removeDuplicates);
93
+ let labels = await _prepareLabels(layer, ids, removeDuplicates);
94
+
95
+ labels =
96
+ // Remove empty lines in labels
97
+ labels.map(labelLines => labelLines.filter(line => line.length > 0))
98
+ // Remove empty labels
99
+ .filter(label => label.length > 0);
84
100
 
85
101
  exportPDF(labels, labelPageDescription);
86
102
 
@@ -95,12 +111,12 @@ export async function downloadPDF(
95
111
  *
96
112
  * @param fieldInfos Layer's fieldInfos structure
97
113
  * @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
98
- * @return Label spec
114
+ * @return Label spec with lines separated by `lineSeparatorChar`
99
115
  */
100
116
  function _convertPopupFieldsToLabelSpec(
101
117
  fieldInfos: __esri.FieldInfo[],
102
118
  bypassFieldVisiblity = false
103
- ): string[] {
119
+ ): string {
104
120
  const labelSpec: string[] = [];
105
121
 
106
122
  // Every visible attribute is used
@@ -112,7 +128,7 @@ function _convertPopupFieldsToLabelSpec(
112
128
  }
113
129
  );
114
130
 
115
- return labelSpec;
131
+ return labelSpec.join(lineSeparatorChar);
116
132
  };
117
133
 
118
134
  /**
@@ -121,26 +137,22 @@ function _convertPopupFieldsToLabelSpec(
121
137
  *
122
138
  * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
123
139
  * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
124
- * @return Label spec
140
+ * @return Label spec with lines separated by `lineSeparatorChar`
125
141
  */
126
142
  function _convertPopupTextToLabelSpec(
127
- popupInfo: string
128
- ): string[] {
129
- // Replace <br>, <br/> with |
130
- popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
143
+ popupInfo: string,
144
+ ): string {
145
+ // Replace <br>, <br/> with the line separator character
146
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, lineSeparatorChar);
131
147
 
132
- // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
148
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, and replace some char representations,
133
149
  // and split the label back into individual lines
134
- let labelSpec = popupInfo
150
+ const labelSpec = popupInfo
135
151
  .replace(/<[\s.]*[^<>]*\/?>/gi, "")
136
152
  .replace(/\xA0/gi, " ")
137
153
  .replace(/&lt;/gi, "<")
138
154
  .replace(/&gt;/gi, ">")
139
- .replace(/&nbsp;/gi, " ")
140
- .split("|");
141
-
142
- // Trim lines and remove empties
143
- labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
155
+ .replace(/&nbsp;/gi, " ");
144
156
 
145
157
  return labelSpec;
146
158
  };
@@ -154,7 +166,7 @@ function _convertPopupTextToLabelSpec(
154
166
  * @return Promise resolving to a set of executors keyed using the expression name
155
167
  */
156
168
  async function _createArcadeExecutors(
157
- labelFormat: string[],
169
+ labelFormat: string,
158
170
  layer: __esri.FeatureLayer
159
171
  ): Promise<IArcadeExecutors> {
160
172
  const arcadeExecutors: IArcadeExecutors = {};
@@ -166,7 +178,7 @@ async function _createArcadeExecutors(
166
178
 
167
179
  // Are there any Arcade expressions in the label format?
168
180
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
169
- const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
181
+ const arcadeExpressionsMatches = labelFormat.match(arcadeExpressionRegExp);
170
182
  if (!arcadeExpressionsMatches) {
171
183
  return Promise.resolve(arcadeExecutors);
172
184
  }
@@ -206,6 +218,44 @@ async function _createArcadeExecutors(
206
218
  );
207
219
  }
208
220
 
221
+ /**
222
+ * Prepares an attribute's value by applying domain and type information.
223
+ *
224
+ * @param attributeValue Value of attribute
225
+ * @param attributeType Type of attribute
226
+ * @param attributeDomain Domain info for attribute, if any
227
+ * @return Attribute value modified appropriate to domain and type
228
+ */
229
+ function _prepareAttributeValue(
230
+ attributeValue: any,
231
+ attributeType: string,
232
+ attributeDomain: __esri.CodedValueDomain | __esri.RangeDomain | __esri.InheritedDomain | null,
233
+ intl: any
234
+ ): any {
235
+ if (attributeDomain && (attributeDomain as __esri.CodedValueDomain).type === "coded-value") {
236
+ // "coded-value" domain field
237
+ const value = (attributeDomain as __esri.CodedValueDomain).getName(attributeValue);
238
+ return value;
239
+ } else {
240
+ // Non-domain field or unsupported domain type
241
+ let value = attributeValue;
242
+ switch (attributeType) {
243
+ case "date":
244
+ // Format date produces odd characters for the space between the time and the AM/PM text,
245
+ // e.g., "12/31/1969, 4:00 PM"
246
+ value = intl.formatDate(value).replace(/\xe2\x80\xaf/g, "");
247
+ break;
248
+ case "double":
249
+ case "integer":
250
+ case "long":
251
+ case "small-integer":
252
+ value = intl.formatNumber(value);
253
+ break;
254
+ }
255
+ return value;
256
+ }
257
+ }
258
+
209
259
  /**
210
260
  * Creates labels from items.
211
261
  *
@@ -229,8 +279,18 @@ async function _prepareLabels(
229
279
  // Get the features to export
230
280
  const featureSet = await queryFeaturesByID(ids, layer);
231
281
 
282
+ // Get field data types. Do we have any domain-based fields?
283
+ const attributeTypes: IAttributeTypes = {};
284
+ const attributeDomains: IAttributeDomains = {};
285
+ layer.fields.forEach(
286
+ field => {
287
+ attributeTypes[field.name] = field.type;
288
+ attributeDomains[field.name] = field.domain;
289
+ }
290
+ );
291
+
232
292
  // Get the label formatting, if any
233
- let labelFormat: string[];
293
+ let labelFormat: string;
234
294
  let arcadeExecutors: IArcadeExecutors = {};
235
295
  if (layer.popupEnabled) {
236
296
  // What data fields are used in the labels?
@@ -243,7 +303,7 @@ async function _prepareLabels(
243
303
  // Can we use the popup title?
244
304
  // eslint-disable-next-line unicorn/prefer-ternary
245
305
  if (typeof layer.popupTemplate.title === "string") {
246
- labelFormat = [layer.popupTemplate.title];
306
+ labelFormat = layer.popupTemplate.title;
247
307
 
248
308
  // Otherwise revert to using attributes
249
309
  } else {
@@ -264,47 +324,60 @@ async function _prepareLabels(
264
324
  // eslint-disable-next-line unicorn/prefer-ternary
265
325
  if (labelFormat) {
266
326
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
327
+ const attributeRegExp = /\{\w+\}/g;
328
+
329
+ // Find the label fields that we need to replace with values
330
+ const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
331
+ const attributeMatches = labelFormat.match(attributeRegExp);
267
332
 
268
- // Convert attributes into an array of labels
333
+ // Convert feature attributes into an array of labels
269
334
  labels = featureSet.features.map(
270
335
  feature => {
271
- const label: string[] = [];
272
- labelFormat.forEach(
273
- labelLineTemplate => {
274
- let labelLine = labelLineTemplate;
275
-
276
- // Replace Arcade expressions
277
- const arcadeExpressionsMatches = labelLine.match(arcadeExpressionRegExp);
278
- if (arcadeExpressionsMatches) {
279
- arcadeExpressionsMatches.forEach(
280
- (match: string) => {
281
- const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
282
- const replacement = arcadeExecutors[expressionName].execute({"$feature": feature});
283
- labelLine = labelLine.replace(match, replacement);
284
- }
285
- )
336
+ let labelPrep = labelFormat;
337
+
338
+ // Replace Arcade expressions
339
+ if (arcadeExpressionMatches) {
340
+ arcadeExpressionMatches.forEach(
341
+ (match: string) => {
342
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
343
+ const value = arcadeExecutors[expressionName].execute({"$feature": feature});
344
+ labelPrep = labelPrep.replace(match, value);
286
345
  }
346
+ )
347
+ }
287
348
 
288
- // Replace fields; must be done after Arcade check because `substitute` will discard Arcade expressions!
289
- labelLine = intl.substitute(labelLine, feature.attributes).trim();
290
-
291
- if (labelLine.length > 0) {
292
- label.push(labelLine);
349
+ // Replace non-Arcade fields
350
+ if (attributeMatches) {
351
+ attributeMatches.forEach(
352
+ (match: string) => {
353
+ const attributeName = match.substring(1, match.length - 1);
354
+ const value = _prepareAttributeValue(feature.attributes[attributeName],
355
+ attributeTypes[attributeName], attributeDomains[attributeName], intl);
356
+ labelPrep = labelPrep.replace(match, value);
293
357
  }
294
- }
295
- )
358
+ )
359
+ }
360
+
361
+ // Split label into lines
362
+ let label = labelPrep.split(lineSeparatorChar);
363
+
364
+ // Trim lines
365
+ label = label.map(line => line.trim());
366
+
296
367
  return label;
297
368
  }
298
- )
299
- // Remove empty labels
300
- .filter(label => label.length > 0);
369
+ );
301
370
 
302
371
  } else {
303
372
  // Export all attributes
304
373
  labels = featureSet.features.map(
305
374
  feature => {
306
- return Object.values(feature.attributes).map(
307
- attribute => `${attribute}`
375
+ return Object.keys(feature.attributes).map(
376
+ (attributeName: string) => {
377
+ const value = _prepareAttributeValue(feature.attributes[attributeName],
378
+ attributeTypes[attributeName], attributeDomains[attributeName], intl);
379
+ return `${value}`;
380
+ }
308
381
  );
309
382
  }
310
383
  );
@@ -324,14 +397,12 @@ async function _prepareLabels(
324
397
  let headerNames = [];
325
398
 
326
399
  if (labelFormat) {
327
- headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
400
+ headerNames = labelFormat.replace(/\{/g, "").replace(/\}/g, "").split(lineSeparatorChar);
328
401
 
329
402
  } else {
330
403
  const featuresAttrs = featureSet.features[0].attributes;
331
404
  Object.keys(featuresAttrs).forEach(k => {
332
- if (featuresAttrs[0].hasOwnProperty(k)) {
333
- headerNames.push(k);
334
- }
405
+ headerNames.push(k);
335
406
  });
336
407
  }
337
408