I am reading in a KmlGroundOverlay that references a tiff file.
I write the KmlGroundOverlay to a a Stream.
I save the Stream to a file.
Everything works as expected except the referenced tiff file is not included in the KMZ.
Is this because I'm importing into the incorrect place when i first read the file?
My KmlGroundOverlays are displayed as excepted over my basemaps in my app.
private async Task LoadAndSaveKmz(StorageFile file, StorageFolder exportFolder)
{
var localFile = await
ApplicationData.Current.TemporaryFolder.CreateFileAsync(
file.Name, Windows.Storage.CreationCollisionOption.ReplaceExisting);
await file.CopyAndReplaceAsync(localFile);
source = new Uri(localFile.Path);
KmlLayer layer = new KmlLayer(source);
await layer.LoadAsync();
//get access to kml node (omitting that code here)
KmlNode node = GetNodeFromLayer(layer);
string filename = "node.kmz";
StorageFile file = await exportFolder.CreateFileAsync(filename,
CreationCollisionOption.ReplaceExisting);
using (Stream stream = await file.OpenStreamForWriteAsync())
{
await node.WriteToAsync(stream);
}
}
Solved! Go to Solution.
changing versions did not help.
but i eventually solved the problem by reading the image file into a WriteableBitmap, writing that bitmap to my ApplicatoinData.Current.TemporaryFolder and then creating KmlGroundOverlay and writing that to the same folder.
code snippet:
public async Task ExportGroundOverlay(StorageFolder folder, WriteableBitmap image, KmlPlacemark placemark, string filename)
{
try
{
//dont know why we need to flip these! but we do for change detection ground overlays.
image = image.Flip(WriteableBitmapExtensions.FlipMode.Horizontal);
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, '_');
}
StorageFile imageFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(filename + ".jpg", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await imageFile.OpenAsync(FileAccessMode.ReadWrite))
{
await image.ToStream(stream, BitmapEncoder.JpegEncoderId);
}
KmlIcon icon = new KmlIcon(new Uri(imageFile.Path));
KmlGroundOverlay groundOverlay = new KmlGroundOverlay(placemark.Geometries[0].Geometry, icon);
StorageFile tempFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(filename + ".kmz", CreationCollisionOption.ReplaceExisting);
using (Stream stream = await tempFile.OpenStreamForWriteAsync())
{
await groundOverlay.WriteToAsync(stream);
}
StorageFile file = await folder.CreateFileAsync(filename + ".kmz", CreationCollisionOption.ReplaceExisting);
await tempFile.CopyAndReplaceAsync(file);
}
catch (Exception e)
{
Debug.WriteLine("ExportGroundOverlay() - exception = " + e.ToString());
}
}
Hello Justin!
I'm guessing that the GroundOverlay references a remote file (i.e. GroundOverlay/Icon/href is an HTTP/HTTPS URL rather than a file path). This would explain why the TIFF is not being saved in the KMZ archive. When a KMLNode is saved, only the URLs to remote files are saved -- the remote content does not downloaded and does not get zipped up. You can find more details about the saving process in the remarks of KmlNode.SaveAsAsync(...) method.
As a workaround, you can edit the KMLGroundOverlay before saving. First get its Icon, then download the Uri to a temporary file, create a new KMLIcon with a path to the temporary file, and finally replace the overlay Icon. Here's an example of how you could do it:
// Ensures that GroundOverlays's image will be saved as an embedded file
private async Task DownloadOverlayImageAsync(KmlGroundOverlay original)
{
var icon = original.Icon;
if (icon == null || icon.Uri == null || !icon.Uri.IsAbsoluteUri)
return; // Skip invalid and already-local overlays
// Create a temporary file to save to.
// Filename is based on the URL, with any invalid characters replaced by "_".
var imageFileName = Regex.Replace(icon.Uri.LocalPath, @"[^a-zA-Z0-9_\-\.]", "_");
var temp = ApplicationData.Current.TemporaryFolder;
var tempSubfolder = await temp.CreateFolderAsync("KmlGroundOverlayDownloads", CreationCollisionOption.OpenIfExists);
var tempFile = await tempSubfolder.CreateFileAsync(imageFileName, CreationCollisionOption.GenerateUniqueName);
// Download using Runtime's HTTP handler (to integrate with AuthenticationManager)
var client = new System.Net.Http.HttpClient(new ArcGISHttpClientHandler());
using (var stream = await client.GetStreamAsync(icon.Uri))
{
using (var fileStream = await tempFile.OpenStreamForWriteAsync())
{
await stream.CopyToAsync(fileStream);
}
}
// Replace existing remote-URL icon with new local-URL icon
var newIcon = new KmlIcon(new Uri(tempFile.Path));
original.Icon = newIcon;
}
Thanks for the detailed response! Lots of good stuff in there.
Unfortunately that is not my case. The image file is within a KMZ that was initially loaded by my App. The entire KMZ is copied to my Temporary Folder within my apps "sandbox".
I suppose i could manually unzip that kmz and move that file to somewhere in my local sandbox, and then specify a relative to path to that location.
So my main question becomes: How should i define the relative path within my new kml? where should the path be relative to such that WriteToSync() works successfully?
Also, is it possible to specify a path such that it finds a a file within another kmz (to prevent need for unzipping).
thanks!
@justinfernandes One more thing to check - which version are you using? If it's not v100.10, can you update to 100.10 and retest?
changing versions did not help.
but i eventually solved the problem by reading the image file into a WriteableBitmap, writing that bitmap to my ApplicatoinData.Current.TemporaryFolder and then creating KmlGroundOverlay and writing that to the same folder.
code snippet:
public async Task ExportGroundOverlay(StorageFolder folder, WriteableBitmap image, KmlPlacemark placemark, string filename)
{
try
{
//dont know why we need to flip these! but we do for change detection ground overlays.
image = image.Flip(WriteableBitmapExtensions.FlipMode.Horizontal);
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, '_');
}
StorageFile imageFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(filename + ".jpg", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await imageFile.OpenAsync(FileAccessMode.ReadWrite))
{
await image.ToStream(stream, BitmapEncoder.JpegEncoderId);
}
KmlIcon icon = new KmlIcon(new Uri(imageFile.Path));
KmlGroundOverlay groundOverlay = new KmlGroundOverlay(placemark.Geometries[0].Geometry, icon);
StorageFile tempFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(filename + ".kmz", CreationCollisionOption.ReplaceExisting);
using (Stream stream = await tempFile.OpenStreamForWriteAsync())
{
await groundOverlay.WriteToAsync(stream);
}
StorageFile file = await folder.CreateFileAsync(filename + ".kmz", CreationCollisionOption.ReplaceExisting);
await tempFile.CopyAndReplaceAsync(file);
}
catch (Exception e)
{
Debug.WriteLine("ExportGroundOverlay() - exception = " + e.ToString());
}
}