diff --git a/changelog/v1.3.1.md b/changelog/v1.3.1.md new file mode 100644 index 00000000..8f947db4 --- /dev/null +++ b/changelog/v1.3.1.md @@ -0,0 +1,19 @@ +# MapCache Desktop - v1.3.1 + +This is a patch release. + +## Bug Fixes + * KML/KMZ GroundOverlay and style fixes. + +## Previous Versions + * [v1.3.0](https://github.com/ngageoint/mapcache-electron/blob/v1.3.0/changelog/v1.3.0.md) + * [v1.2.0](https://github.com/ngageoint/mapcache-electron/blob/v1.2.0/changelog/v1.2.0.md) + * [v1.1.1](https://github.com/ngageoint/mapcache-electron/blob/v1.1.1/changelog/v1.1.1.md) + * [v1.1.0](https://github.com/ngageoint/mapcache-electron/blob/v1.1.0/changelog/v1.1.0.md) + * [v1.0.9](https://github.com/ngageoint/mapcache-electron/blob/v1.0.9/changelog/v1.0.9.md) + * [v1.0.8](https://github.com/ngageoint/mapcache-electron/blob/v1.0.8/changelog/v1.0.8.md) + * [v1.0.7](https://github.com/ngageoint/mapcache-electron/blob/v1.0.7/changelog/v1.0.7.md) + * [v1.0.6](https://github.com/ngageoint/mapcache-electron/blob/v1.0.6/changelog/v1.0.6.md) + * [v1.0.5](https://github.com/ngageoint/mapcache-electron/blob/v1.0.5/changelog/v1.0.5.md) + * [v1.0.4](https://github.com/ngageoint/mapcache-electron/blob/v1.0.5/changelog/v1.0.4.md) + * [v1.0.3](https://github.com/ngageoint/mapcache-electron/blob/v1.0.5/changelog/v1.0.3.md) diff --git a/package.json b/package.json index a9f83707..a4a6bb18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcache", - "version": "1.3.0", + "version": "1.3.1", "description": "Desktop Electron app to create GeoPackages from geospatial data sources.", "author": "Christopher Caldwell ", "scripts": { diff --git a/src/lib/source/kml/KMLSource.js b/src/lib/source/kml/KMLSource.js index 65b6fd8a..09868d21 100644 --- a/src/lib/source/kml/KMLSource.js +++ b/src/lib/source/kml/KMLSource.js @@ -105,7 +105,6 @@ export default class KMLSource extends Source { const kmlDirectory = path.dirname(this.filePath) const name = path.basename(this.filePath, path.extname(this.filePath)) - statusCallback('Parsing and storing features', 0) const groundOverlays = [] @@ -122,7 +121,6 @@ export default class KMLSource extends Source { }, styleMap => { styleMaps[styleMap.id] = styleMap }) - const featureStatusMax = 100 - groundOverlays.length if (featureCount > 0) { @@ -141,9 +139,9 @@ export default class KMLSource extends Source { for (let i = 0; i < styleIds.length; i++) { const styleId = styleIds[i] const style = styles[styleId] - if (style.href != null) { + if (style.hasIcon) { try { - const icon = await this.generateIconFromKmlIconStyle(style, kmlDirectory, tmpDir, 'icon_' + iconFileNameCount) + const icon = await this.generateIconFromKmlIconStyle(style.icon, kmlDirectory, tmpDir, 'icon_' + iconFileNameCount) if (icon != null) { iconIdMap[styleId] = addIcon(icon) iconFileNameCount++ @@ -152,7 +150,9 @@ export default class KMLSource extends Source { // eslint-disable-next-line no-console console.error('Failed to generate icon.') } - } else { + } + + if (style.hasLine || style.hasPoly) { style.name = style.id styleIdMap[styleId] = addStyle(style) } @@ -167,17 +167,18 @@ export default class KMLSource extends Source { const featureRowId = addFeature(feature) if (styleRef != null) { // if ref is for a map, select the normal style reference - if (styleMaps[styleRef] != null) { + const styleRefs = [styleRef] + while (styleMaps[styleRef] != null) { styleRef = styleMaps[styleRef].normal + styleRefs.push(styleRef) } // check if it is a style or icon - if (styleIdMap[styleRef] != null) { - const styleRowId = styleIdMap[styleRef] - setFeatureStyle(featureRowId, feature.geometry.type, styleRowId) - - } else if (iconIdMap[styleRef] != null) { + if ((feature.geometry.type === 'Point' || feature.geometry.type === 'MultiPoint') && iconIdMap[styleRef] != null) { const iconRowId = iconIdMap[styleRef] setFeatureIcon(featureRowId, feature.geometry.type, iconRowId) + } else if (styleIdMap[styleRef] != null) { + const styleRowId = styleIdMap[styleRef] + setFeatureStyle(featureRowId, feature.geometry.type, styleRowId) } } diff --git a/src/lib/source/kml/KmlStream.js b/src/lib/source/kml/KmlStream.js index 497baba1..4ff37749 100644 --- a/src/lib/source/kml/KmlStream.js +++ b/src/lib/source/kml/KmlStream.js @@ -189,7 +189,6 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { let styleMap = null let pair = null let inPlacemark = false - let inStyle = false let hasStyleUrl = false const saxStream = sax.createStream(false, { lowercase: true @@ -199,20 +198,13 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { currentTag = tag switch (tag.name) { case 'style': - inStyle = true - style = {} - if (tag.attributes.id != null) { - style.id = '#' + tag.attributes.id - } else { - style.id = '#' + nextId() + style = { + id: '#' + (tag.attributes.id != null ? tag.attributes.id : nextId()) } return case 'stylemap': - styleMap = {} - if (tag.attributes.id != null) { - styleMap.id = '#' + tag.attributes.id - } else { - styleMap.id = '#' + nextId() + styleMap = { + id: '#' + (tag.attributes.id != null ? tag.attributes.id : nextId()) } return case 'pair': @@ -232,17 +224,22 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { return case 'linestyle': styleMode = 'line' + if (style != null) { + style.hasLine = true + } return case 'polystyle': styleMode = 'poly' + if (style != null) { + style.hasPoly = true + } return case 'iconstyle': styleMode = 'icon' - if (inPlacemark) { - icon = {id: style.id} - } else { - icon = {id: style.id} + if (style != null) { + style.hasIcon = true } + icon = {} return case 'groundoverlay': groundOverlay = {} @@ -298,6 +295,7 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { } isMulti++ geoms = [] + return } }) saxStream.on('text', (data) => { @@ -310,17 +308,46 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { return case 'color': if (groundOverlay != null) { - groundOverlay.alpha = Math.round(parseInt(data.substring(0, 2), 16) / 255) + try { + groundOverlay.alpha = Math.round(parseInt(data.substring(0, 2), 16) / 255) + // eslint-disable-next-line no-unused-vars + } catch (e) { + groundOverlay.alpha = 1.0 + } } else if (style != null) { - const alpha = Math.round(parseInt(data.substring(0, 2), 16) / 255) - const blue = data.substring(2, 4) - const green = data.substring(4, 6) - const red = data.substring(6) + let alpha = 1.0 + let blue = '00' + let green = '00' + let red = '00' + try { + if (data.length === 3) { + alpha = 1.0 + blue = data.substring(0, 1) + data.substring(0, 1) + green = data.substring(1, 2) + data.substring(1, 2) + red = data.substring(2) + data.substring(2) + } else if (data.length === 4) { + alpha = parseInt(data.substring(0, 1) + data.substring(0, 1), 16) / 255 + blue = data.substring(1, 2) + data.substring(1, 2) + green = data.substring(2, 3) + data.substring(2, 3) + red = data.substring(3) + data.substring(3) + } else if (data.length === 6) { + alpha = 1.0 + blue = data.substring(0, 2) + green = data.substring(2, 4) + red = data.substring(4) + } else if (data.length === 8) { + alpha = parseInt(data.substring(0, 2), 16) / 255.0 + blue = data.substring(2, 4) + green = data.substring(4, 6) + red = data.substring(6) + } + // eslint-disable-next-line no-empty, no-unused-vars + } catch (e) {} if (styleMode === 'line') { - style.color = '#' + red + blue + green + style.color = '#' + red + green + blue style.opacity = alpha } else if (styleMode === 'poly') { - style.fillColor = '#' + red + blue + green + style.fillColor = '#' + red + green + blue style.fillOpacity = alpha } } @@ -343,6 +370,8 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { case 'name': if (groundOverlay != null) { groundOverlay.name = data + } else if (inPlacemark) { + props.name = data } return case 'scale': @@ -407,7 +436,7 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { coordsText += data return case 'styleurl': - hasStyleUrl = true + hasStyleUrl = inPlacemark if (pair != null) { pair.styleUrl = data } else { @@ -435,45 +464,49 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { pair = null return case 'style': - inStyle = false - if (!hasStyleUrl) { + if (style != null && !hasStyleUrl) { + if (fillStyle === 0 && style.fillOpacity != null) { + style.fillOpacity = 0.0 + } + if (outlineStyle === 0 && style.opacity != null) { + style.opacity = 0.0 + } + if (style.width == null) { + style.width = 2.0 + } if (icon != null) { - if (pair != null) { - pair.style = icon - } else if (inPlacemark) { - styleId = icon.id - } - onStyle(icon) - icon = null - } else if (style != null) { - if (fillStyle === 0 && style.fillOpacity != null) { + style.icon = icon + } + if (pair != null) { + pair.style = style + } + if (inPlacemark) { + styleId = style.id + } + if (style.hasLine || style.hasPoly) { + if (style.fillOpacity == null) { style.fillOpacity = 0.0 } - if (outlineStyle === 0 && style.opacity != null) { - style.opacity = 0.0 + if (style.fillColor == null) { + style.fillColor = '#000000' } - if (style.width == null) { - style.width = 2.0 + if (style.opacity == null) { + style.opacity = 1.0 } - if (pair != null) { - pair.style = style - } if (inPlacemark) { - styleId = style.id + if (style.color == null) { + style.color = '#000000' } - onStyle(style) - style = null } - } else { - // TODO: handle merged style - style = null - icon = null + onStyle(style) } + style = null + icon = null return case 'stylemap': - onStyleMap(styleMap) if (inPlacemark) { styleId = styleMap.id } + onStyleMap(styleMap) styleMap = null return case 'linestyle': @@ -512,10 +545,7 @@ function streamKml (filePath, onFeature, onGroundOverlay, onStyle, onStyleMap) { // eslint-disable-next-line no-case-declarations const out = { type: 'Feature', - properties: folder ? { - folder: folder, - ...props - } : props, + properties: props, geometry: null } if (styleId != null) { diff --git a/src/lib/util/kml/KMLUtilities.js b/src/lib/util/kml/KMLUtilities.js index 47ff297a..ff67d082 100644 --- a/src/lib/util/kml/KMLUtilities.js +++ b/src/lib/util/kml/KMLUtilities.js @@ -4,7 +4,7 @@ import { BoundingBox } from '@ngageoint/geopackage' import bbox from '@turf/bbox' import transformRotate from '@turf/transform-rotate' import isNil from 'lodash/isNil' -import * as GeoTIFF from 'geotiff' +import { writeArrayBuffer } from 'geotiff' import GeoTIFFSource from '../../source/geotiff/GeoTIFFSource' import { getRemoteImage } from '../../network/NetworkRequestUtils' import { createCanvas, disposeCanvas, disposeImage, makeImage } from '../canvas/CanvasUtilities' @@ -27,14 +27,18 @@ async function convert4326ImageToGeoTIFF (filePath, geotiffFilePath, extent) { const imageData = context.getImageData(0, 0, image.width(), image.height()) const values = new Uint8Array(imageData.data) const metadata = { - height: image.height(), - width: image.width(), + ImageLength: image.height(), + ImageWidth: image.width(), + Compression: 1, // no compression ModelPixelScale: [(extent[2] - extent[0]) / image.width(), (extent[3] - extent[1]) / image.height(), 0], ModelTiepoint: [0, 0, 0, extent[0], extent[3], 0], - PhotometricInterpretation: 2 + PhotometricInterpretation: 2, + GeographicTypeGeoKey: 4326, + GeogCitationGeoKey: 'WGS 84', + GTModelTypeGeoKey: 2, } - GeoTIFF.writeArrayBuffer(values, metadata).then(arrayBuffer => { - fs.writeFile(geotiffFilePath, Buffer.from(arrayBuffer), function (err) { + try { + fs.writeFile(geotiffFilePath, Buffer.from(writeArrayBuffer(values, metadata)), function (err) { if (err) { // eslint-disable-next-line no-console console.error('Failed to write GeoTIFF for KML Ground Overlay.') @@ -44,14 +48,13 @@ async function convert4326ImageToGeoTIFF (filePath, geotiffFilePath, extent) { } }) // eslint-disable-next-line no-unused-vars - }).catch(e => { + } catch (e) { // eslint-disable-next-line no-console console.error('Failed to convert kml ground overlay into 4326 GeoTIFF image.') - resolve(false) - }).finally(() => { disposeImage(image) disposeCanvas(canvas) - }) + resolve(false) + } }) }) } diff --git a/src/views/Documentation/ReleaseNotes.vue b/src/views/Documentation/ReleaseNotes.vue index 063d96b7..551d0a45 100644 --- a/src/views/Documentation/ReleaseNotes.vue +++ b/src/views/Documentation/ReleaseNotes.vue @@ -79,6 +79,13 @@ export default { data() { return { releaseNotes: [ + { + title: 'MapCache 1.3.1', + releaseDate: 'Mar. 21, 2022', + bugFixes: [ + 'KML/KMZ GroundOverlay and style fixes.', + ] + }, { title: 'MapCache 1.3.0', releaseDate: 'Mar. 14, 2022',