GM_ExportRaster / GM_ExportRasterEx referencing more than 1 layer
Hi,
Assume I load 4 different rectified image files (MrSID, TIFF, JPEG, etc.) into 4 separate GM_LayerHandle_t32* (layerLists).
GM_ExportRaster / GM_ExportRasterEx allows me to specify an AOI / rectangle / bounds to export only a segment of ONE of those 4 layers (via one layerlist pointer - GM_LayerHandle_t32*). However, in the Global Mapper GUI, I could manually specify or draw a rectangle across 2,3, or all 4 of the loaded layers and it could export an image that would span all of the selected layers - also taking into account any z-ordering I believe.
I have yet to discover how to do this programatically, as it seems there is an SDK limitation to only specify one layer/layerlist at a time. Is this the only programatic method available to export a raster? If so, I could envision testing/scanning the world bounds of loaded layers and then just select one automatically. Unfortunately, any AOIS/bounds that span between layers would then have to be stitched together "manually" by my program.
Thoughts on a better way to accomplish this by being able to specify more than one loaded layer/layerlist at a time? Do I need to LOAD the layers differently into a larger layerlist? (not sure how to do that).
Thank you!
Brian
Assume I load 4 different rectified image files (MrSID, TIFF, JPEG, etc.) into 4 separate GM_LayerHandle_t32* (layerLists).
GM_ExportRaster / GM_ExportRasterEx allows me to specify an AOI / rectangle / bounds to export only a segment of ONE of those 4 layers (via one layerlist pointer - GM_LayerHandle_t32*). However, in the Global Mapper GUI, I could manually specify or draw a rectangle across 2,3, or all 4 of the loaded layers and it could export an image that would span all of the selected layers - also taking into account any z-ordering I believe.
I have yet to discover how to do this programatically, as it seems there is an SDK limitation to only specify one layer/layerlist at a time. Is this the only programatic method available to export a raster? If so, I could envision testing/scanning the world bounds of loaded layers and then just select one automatically. Unfortunately, any AOIS/bounds that span between layers would then have to be stitched together "manually" by my program.
Thoughts on a better way to accomplish this by being able to specify more than one loaded layer/layerlist at a time? Do I need to LOAD the layers differently into a larger layerlist? (not sure how to do that).
Thank you!
Brian
Comments
-
Brian,
When you load a file and it returns a layer list that is just the list of layers loaded by that one load operation. You should copy the layer handles out of that list and put them in your own list of everything that is loaded, or if you have the latest SDK use the GM_GetLoadedLayerList function to get the list of all currently loaded layers. If you look at the C++ sample application whenever it loads layers it copies the layer handles to it's own list of layer handles. Those are then available to pass in to exports and other operations, like draws, in whatever order you need.
Thanks,
Mike
Global Mapper Guru
geohelp@bluemarblegeo.com
Blue Marble Geographics for Coordinate Conversion, Image Reprojection and Vector Translation -
Mike,
This makes sense. Let me take a look - I need to figure out how to abstract a list of IntPtr via C#, but I think I should be able to do that. Worst case, I just write that part in C++ instead (then abstract that to C# via CLI or P/invoke).
Brian -
Brian,
The C# sample application included with the SDK also stores a list of loaded layers and extracts that from the list returned by GM_LoadLayerList, so just use that as a reference.
Thanks,
Mike
Global Mapper Guru
geohelp@bluemarblegeo.com
Blue Marble Geographics for Coordinate Conversion, Image Reprojection and Vector Translation -
Mike,
Yes - I totally forgot about that in the example. Thanks.
One thing to note that I just realized, in 64-bit applications, pointers are 8 bytes. So your example c# code line of:
IntPtr theLayerHandlePtr = (IntPtr)((UInt32)theLayerList + i * 4);
should be
IntPtr theLayerHandlePtr = (IntPtr)((UInt32)theLayerList + i * 8);
when using x64 compilation.
Brian -
Brian,
Ah yes that part of the sample is very old. I have changed the 4 to IntPtr.Size so it should work on either platform seamlessly now.
Thanks,
Mike
Global Mapper Guru
geohelp@bluemarblegeo.com
Blue Marble Geographics for Coordinate Conversion, Image Reprojection and Vector Translation -
Mike,
Thanks - that worked perfectly.
For the lazy - here's basically what I did:
I created my own type to hold my layer stuffs of types of Raster and Elevation (or more when I feel like it - this example shows Elevation types):public class GmLoadedDataLayer { public IntPtr LayerHandle { get; set; } public LayerType LayerType { get; set; } public GM_LayerInfo_t LayerInfo { get; set; } }
LayerType just looks like this:public enum LayerType { Elevation, Terrain, Other }
and a new List<T> to hold them:public List<GmLoadedDataLayer> GmLoadedDataLayers = new List<GmLoadedDataLayer>();
My LoadLayerEx struct looks like this:[UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate GM_Error_t32 GM_LoadLayerListEx(string aFilename, out IntPtr aLayerList, out uint aNumLoadedLayers, GM_LoadFlags_t32 aLoadFlags, string aExtraLoadOptions);
My abstracted wrapper looks like this:public int LoadLayerListEx(string path, out IntPtr dataLayerList, out uint dataLayerCount, out string gmErrorDesc) { var loadLayerListEx = (GlobalMapper.GM_LoadLayerListEx)Marshal.GetDelegateForFunctionPointer(NativeMethods.GetProcAddress(GlobalMapperInterfaceDllAddress, typeof(GlobalMapper.GM_LoadLayerListEx).Name), typeof(GlobalMapper.GM_LoadLayerListEx)); const GM_LoadFlags_t32 flags = GM_LoadFlags_t32.GM_LoadFlags_HideAllPrompts | GM_LoadFlags_t32.GM_LoadFlags_HideProgress; var lastGmError = loadLayerListEx(path, out dataLayerList, out dataLayerCount, flags, string.Empty); gmErrorDesc = lastGmError.GetDescription(); return (int)lastGmError; }
Then call that wrapper like this to add 1 to n layers based on file paths:IntPtr dataLayerList; uint dataLayerCount = 0; string gmErrorDesc; var result = LoadLayerListEx(filePathToLoad, out dataLayerList, out dataLayerCount, out gmErrorDesc); if (result != (int)GM_Error_t32.GM_Error_None) return gmErrorDesc; // Add each layer that was just loaded from that one file path for (var i = 0; i < dataLayerCount; i++) { // Extract the layer handle from the list var dataLayerHandlePtr = (IntPtr)((uint)dataLayerList + i * IntPtr.Size); var dataLayerHandle = (IntPtr)Marshal.PtrToStructure(dataLayerHandlePtr, typeof(IntPtr)); // Add the layer var dataLayerInfo = new GM_LayerInfo_t(); var resultString = GetLayerInfo(dataLayerHandle, out dataLayerInfo); if (!string.IsNullOrWhiteSpace(resultString)) return resultString; GmLoadedDataLayers.Add(new GmLoadedDataLayer { LayerType = LayerType.Elevation, LayerHandle = dataLayerHandle, LayerInfo = dataLayerInfo }); }
Then the final call to do stuff here:var elevationLayers = GmLoadedDataLayers.Where(x => x.LayerType == LayerType.Elevation).ToList(); var elevationLayersCount = elevationLayers.Count(); if (elevationLayersCount <= 0) return "No loaded Elevation Layers Found."; // allocate a buffer to hold layer list var layerListPtr = new IntPtr(); try { var layerHandle = new IntPtr(); if (elevationLayersCount > 0) layerListPtr = Marshal.AllocCoTaskMem((int)(elevationLayersCount * Marshal.SizeOf(layerHandle))); // fill the layer list for (var i = 0; i < elevationLayersCount; i++) { // extract the data from the buffer layerHandle = (IntPtr)elevationLayers[i].LayerHandle; var destinationPtr = (IntPtr)((uint)layerListPtr + i * Marshal.SizeOf(layerHandle)); Marshal.StructureToPtr(layerHandle, destinationPtr, false); } var gmResult = WhateverGMCommandYouWantToRunHere - passing in layerListPtr as aLayerList; if (!gmResult.Equals(GM_Error_t32.GM_Error_None.GetDescription())) return gmResult; } catch (Exception ex) { return ex.Message; } finally { // Free the layer list memory if (elevationLayersCount > 0) Marshal.FreeCoTaskMem(layerListPtr); }
That's more or less it, in a nutshell. I hope this is helpful and not confusing.
Brian
Categories
- 12.5K All Categories
- 5.5K Features Discussion
- 314 Downloading Imagery
- 1.3K Elevation Data
- 377 Georeferencing Imagery Discussion
- 611 GM Script Language
- 50 User Scripts
- 112 GPS Features
- 397 Projection Questions
- 803 Raster Data
- 1.3K Vector Data
- 6.5K Support
- 161 Announcement and News
- 893 Bug Report
- 557 SDK
- 1.2K Suggestion Box
- 3.7K Technical Support
- 542 Other Discussion
- 128 GIS Data Sources
- 26 Global Mapper Showcase
- 229 How I use Global Mapper
- 104 Global Mapper Forum Website