|
BLOG
|
Given the fact that the ArcGIS Runtime SDK for .NET supports group layers at 100.5, and that they work perfectly fine in 2D maps, it's somewhat disappointing that mobile maps with group layers are still not hydrated properly. Esri can mansplain their issues until their faces turn blue, but the fact is that the tools exist right now to do it. In this post I will show you a functional workaround. Before I start showing any code, let's take a peek at the internal structure of a mobile map package, as produced by ArcGIS Pro 2.3.2: The .info file is a JSON file, and it's important because it provides the link between the map index and the map name: "maps": [ "Mohave Electric MMPK" ] The "Create Mobile Map Package" tool enforces unique map names, so the problem of duplicate map names should never arise. My own practice is to have only one map per MMPK for operational layers only, no base layers. This allows separation of packages based on frequency of updates: facility data (frequent updates), landbase (infrequent updates), and basemaps (vector or raster tile packages). A Runtime app can then open each package, grab its layers, and add them to the main map. Now let's look at the p14 subfolder: The .mapx file is a JSON file containing all the information needed for ArcGIS Pro to hydrate the map; it uses the CIM (Cartographic Information Model) specification. In an ideal world, that would be the file that Runtime uses to open a map, but it's not. Runtime uses the .mmap file to open maps: it's also a JSON file, but it doesn't use CIM. Although the mobile map specification is not fully aligned with CIM, nonetheless it does store group layer information: {
"id" : "b4a38ad5025b46d1ac809b3ed5258935",
"title" : "Transformers",
"visibility" : true,
"layerType" : "GroupLayer",
"layers" : [
{
"id" : "2c4d3bafe82c4344a0bc2019b67097cc",
"title" : "Capacitor Bank",
"visibility" : true,
"layerType" : "ArcGISFeatureLayer",
"layerDefinition" : {
"minScale" : 4000
},
What it does not store, however, is any minScale or maxScale information for the group layer. You can look that up in the .mapx file, as long as the group layers are uniquely named, but I don't bother. The workaround is to design the mobile map without scale ranges on the group layers. I've attached an example Visual Studio project which retrieves the .info and .mmap files to extract the operational layer definitions: // Open mmpk as zip archive
string sMapName = null;
object[] oplayers = null;
using (ZipArchive zip = ZipFile.Open(sPath, ZipArchiveMode.Read))
{
// Parse the info file to get map name
string sInfoFile = Path.GetFileNameWithoutExtension(sPath) + ".info";
sMapName = GetMapName(zip, sInfoFile, iMapIndex);
// Parse corresponding .mmap file and get operational layers
if (!string.IsNullOrEmpty(sMapName))
{
string sMapFile = "p14/" + sMapName + ".mmap";
oplayers = GetOperationalLayers(zip, sMapFile);
}
}
The code uses the .NET JavaScriptSerializer object to parse the JSON: private static string GetMapName(ZipArchive zip, string sInfoFile, int iMapIndex)
{
string sJSON = GetEntry(zip, sInfoFile);
if (string.IsNullOrEmpty(sJSON))
return null;
Dictionary<string, object> dict = js.DeserializeObject(sJSON) as Dictionary<string, object>;
if (!dict.ContainsKey("maps"))
return null;
if (!(dict["maps"] is object[] maps))
return null;
if (iMapIndex > maps.Count() - 1)
return null;
return maps[iMapIndex].ToString();
}
private static object[] GetOperationalLayers(ZipArchive zip, string sMapFile)
{
string sJSON = GetEntry(zip, sMapFile);
if (string.IsNullOrEmpty(sJSON))
return null;
Dictionary<string, object> dict = js.DeserializeObject(sJSON) as Dictionary<string, object>;
if (!dict.ContainsKey("map"))
return null;
if (!(dict["map"] is Dictionary<string, object> map))
return null;
if (!map.ContainsKey("operationalLayers"))
return null;
return map["operationalLayers"] as object[];
}
private static string GetEntry(ZipArchive zip, string sEntryName)
{
ZipArchiveEntry zipInfo = zip.GetEntry(sEntryName);
if (zipInfo == null)
return null;
string sJSON = null;
using (StreamReader reader = new StreamReader(zipInfo.Open()))
{
sJSON = reader.ReadToEnd();
}
return sJSON;
}
Then it opens the map and restructures it to insert group layers in the appropriate places. private static void AddLayers(Map MyMap, GroupLayer glParent, object[] sublayers)
{
foreach (object obj in sublayers)
{
if (!(obj is Dictionary<string, object> layer))
continue;
if (!layer.ContainsKey("layerType"))
continue;
string sLayerType = layer["layerType"].ToString();
// Process layer
Layer lyr = null;
if (sLayerType == "GroupLayer")
{
// Create group layer
// **** LIMITATION: .mmap group layer entry does not store minScale or maxScale ****
// [MMPKs should be created with that limitation in mind]
GroupLayer glChild = new GroupLayer();
string sDisplayName = "";
if (layer.ContainsKey("title"))
sDisplayName = layer["title"].ToString();
glChild.Name = sDisplayName;
bool bVisibility = true;
if (layer.ContainsKey("visibility"))
bVisibility = (bool)layer["visibility"];
glChild.IsVisible = bVisibility;
if (!layer.ContainsKey("layers"))
continue;
if (!(layer["layers"] is object[] layers))
continue;
AddLayers(null, glChild, layers);
lyr = glChild;
}
else
{
// Get layer and add to parent
if (!layer.ContainsKey("id"))
continue;
string sID = layer["id"].ToString();
if (!OpLayers.ContainsKey(sID))
continue;
lyr = OpLayers[sID];
}
if (lyr == null)
continue;
if (glParent == null)
MyMap.OperationalLayers.Add(lyr);
else
glParent.Layers.Add(lyr);
}
}
As a bonus, the example code also builds an interactive TOC. Enjoy! [P.S. -- I've heard rumors that ArcGIS Pro 2.4 will introduce potentially code-breaking changes to the mobile map specification. Watch out for that.]
... View more
04-16-2019
10:57 AM
|
0
|
3
|
1743
|
|
POST
|
Huh, I thought my partial modes were working, but I guess I didn't explore thoroughly enough. Thanks!
... View more
04-15-2019
03:30 PM
|
0
|
0
|
1497
|
|
POST
|
Looks like I need to use a different overload of StartAsync and pass in the EditConfiguration.
... View more
04-15-2019
02:21 PM
|
0
|
0
|
1497
|
|
POST
|
Starting at 100.5, the EditConfiguration of the SketchEditor of the MapView is null, which causes my 100.4 code to crash. How can I get it to be populated so that I can manipulate it?
... View more
04-15-2019
02:13 PM
|
0
|
3
|
1659
|
|
BLOG
|
Granted, it's a lame Internet meme, but it got your attention, no? My argument rests in a single word: granularity. If you picked up on that, you need not read the rest of my post. For those of you who need one or two concrete cases, read on. 1) You may not need me to tell you that containment associations simply fail to replace Smallworld's internal worlds. Internal worlds allow an independent coordinate system, which I suppose requires a way to trace between feature datasets, and at this point I need say no more. 2) That aside, here's a nifty little diagnostic tool -- stolen from Smallworld -- that I implemented for the old geometric network, and I challenge Esri to allow me to duplicate it for the new utility network. This is how it works in ArcMap: you click a button, and it displays the actual edge connectivity for junction features in the current map view. As far as I can tell, this functionality is not possible on a utility network via the Pro/REST/Enterprise APIs. You can access the ancient code here: http://www.pierssen.com/arcgis10/datatools.htm And I'll leave it to you kids to prove me wrong. To be fair, I did hear, from certain folks at the DevSummit, rumors of a ForwardStar-ish implementation to come, but hey guess what, it's not here yet. Hence my original point.
... View more
04-04-2019
10:50 AM
|
1
|
0
|
706
|
|
POST
|
I meant that the wristband stations were not set up by 10 AM, and after waiting most of the break, I finally gave up and went to the next session before time ran out.
... View more
04-03-2019
11:41 AM
|
0
|
0
|
948
|
|
DOC
|
FYI, here's another heads up in case you haven't spotted it already: the UPDM_PressureRange domain uses GEOMETRY_RATIO for split policy, which doesn't make sense in the context of MAOP. It should be set to DUPLICATE.
... View more
04-02-2019
12:50 PM
|
0
|
0
|
6084
|
|
POST
|
Yes, I did, using the manual tool in Pro 2.3.1. Now I'm working on a Python script to automate the file GDB massaging and MMPK creation. It's no big deal for me to delete the anno relationship classes, I just wanted to give a heads up that I'm still seeing the issue.
... View more
03-20-2019
12:43 PM
|
0
|
0
|
798
|
|
POST
|
Sorry, I didn't make it clear that I did upgrade the annotation. I realize that it won't display at 100.4, but I'm creating a workflow in anticipation of 100.5.
... View more
03-20-2019
12:16 PM
|
0
|
2
|
798
|
|
POST
|
Supposedly this bug was fixed at 2.1, but at 2.3 I'm still having issues creating a mobile map package from a feature dataset containing an annotation relationship class. If I delete the relationship class, the problem goes away.
... View more
03-20-2019
11:45 AM
|
0
|
4
|
877
|
|
POST
|
I notice that when I set the substitute symbol color for an annotation layer, Pro 2.3 clears out any properties that I just set for that layer (name, display scale, definition query). Has anyone else seen this?
... View more
03-19-2019
09:51 AM
|
0
|
1
|
1062
|
|
POST
|
I also noticed a downgrade in line symbols read by Runtime, going from 2.2 to 2.3. Will the patch address this as well?
... View more
03-15-2019
03:24 PM
|
0
|
1
|
2715
|
|
POST
|
Fine. I've attached a simple example demonstrating my current workaround.
... View more
03-14-2019
12:20 PM
|
0
|
0
|
1647
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 01-04-2012 06:42 AM | |
| 1 | 09-23-2021 10:42 AM | |
| 2 | 09-28-2021 07:07 AM | |
| 1 | 04-07-2021 10:31 PM | |
| 3 | 03-21-2021 01:14 PM |
| Online Status |
Offline
|
| Date Last Visited |
01-07-2022
08:31 AM
|