diff --git a/src/ArcGIS.AllSampleViewers.sln b/src/ArcGIS.AllSampleViewers.sln index 11f4ec9732..759eefb4c7 100644 --- a/src/ArcGIS.AllSampleViewers.sln +++ b/src/ArcGIS.AllSampleViewers.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArcGIS.WPF.Viewer.Net", "WP EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArcGIS.WinUI.Viewer", "WinUI\ArcGIS.WinUI.Viewer\ArcGIS.WinUI.Viewer.csproj", "{F39D6B3E-4E1D-4211-B152-59F8F71C8544}" EndProject -Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "ArcGIS.WinUI.Viewer (Package)", "WinUI\ArcGIS.WinUI.Viewer (Package)\ArcGIS.WinUI.Viewer (Package).wapproj", "{BA503BD0-660A-4EA6-AB0E-11823F9BAD96}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MAUI", "MAUI", "{5EA55853-597D-4ED0-BAF9-6B737EBC5AC1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArcGIS.Samples.Maui", "MAUI\Maui.Samples\ArcGIS.Samples.Maui.csproj", "{7F7B3255-9EA0-4296-B690-14BE9E2204F3}" @@ -21,49 +19,19 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ArcGIS.Samples.Shared", "Sa EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Ad-Hoc|Any CPU = Ad-Hoc|Any CPU - Ad-Hoc|ARM64 = Ad-Hoc|ARM64 - Ad-Hoc|iPhone = Ad-Hoc|iPhone - Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator - Ad-Hoc|x64 = Ad-Hoc|x64 - Ad-Hoc|x86 = Ad-Hoc|x86 - AppStore|Any CPU = AppStore|Any CPU - AppStore|ARM64 = AppStore|ARM64 - AppStore|iPhone = AppStore|iPhone - AppStore|iPhoneSimulator = AppStore|iPhoneSimulator - AppStore|x64 = AppStore|x64 - AppStore|x86 = AppStore|x86 Debug|Any CPU = Debug|Any CPU Debug|ARM64 = Debug|ARM64 - Debug|iPhone = Debug|iPhone - Debug|iPhoneSimulator = Debug|iPhoneSimulator Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|ARM64 = Release|ARM64 - Release|iPhone = Release|iPhone - Release|iPhoneSimulator = Release|iPhoneSimulator Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|ARM64.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|x64.ActiveCfg = Release|x64 - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Ad-Hoc|x86.ActiveCfg = Release|x86 - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|ARM64.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|x64.ActiveCfg = Release|x64 - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.AppStore|x86.ActiveCfg = Release|x86 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|x64.ActiveCfg = Debug|x64 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|x64.Build.0 = Debug|x64 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Debug|x86.ActiveCfg = Debug|x86 @@ -71,102 +39,30 @@ Global {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|Any CPU.Build.0 = Release|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|ARM64.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|iPhone.ActiveCfg = Release|Any CPU - {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|x64.ActiveCfg = Release|x64 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|x64.Build.0 = Release|x64 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|x86.ActiveCfg = Release|x86 {FE1B81E3-59BE-4632-A1EB-12FF19F4B59A}.Release|x86.Build.0 = Release|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|Any CPU.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|Any CPU.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|ARM64.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|ARM64.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|iPhone.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|iPhone.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|x64.ActiveCfg = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|x64.Build.0 = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|x86.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Ad-Hoc|x86.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|Any CPU.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|Any CPU.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|ARM64.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|ARM64.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|iPhone.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|iPhone.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|iPhoneSimulator.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|iPhoneSimulator.Build.0 = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|x64.ActiveCfg = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|x64.Build.0 = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|x86.ActiveCfg = Debug|x86 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.AppStore|x86.Build.0 = Debug|x86 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|Any CPU.ActiveCfg = Debug|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|ARM64.ActiveCfg = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|iPhone.ActiveCfg = Debug|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|iPhoneSimulator.ActiveCfg = Debug|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x64.ActiveCfg = Debug|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x64.Build.0 = Debug|x64 + {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x64.Deploy.0 = Debug|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x86.ActiveCfg = Debug|x86 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x86.Build.0 = Debug|x86 + {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Debug|x86.Deploy.0 = Debug|x86 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|Any CPU.ActiveCfg = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|Any CPU.Build.0 = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|ARM64.ActiveCfg = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|ARM64.Build.0 = Release|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|iPhone.ActiveCfg = Release|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|iPhone.Build.0 = Release|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|iPhoneSimulator.ActiveCfg = Release|x64 - {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|iPhoneSimulator.Build.0 = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|x64.ActiveCfg = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|x64.Build.0 = Release|x64 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|x86.ActiveCfg = Release|x86 {F39D6B3E-4E1D-4211-B152-59F8F71C8544}.Release|x86.Build.0 = Release|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|Any CPU.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|Any CPU.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|Any CPU.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|ARM64.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|ARM64.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|ARM64.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhone.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhone.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhone.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x64.ActiveCfg = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x64.Build.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x64.Deploy.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x86.ActiveCfg = Debug|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x86.Build.0 = Debug|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Ad-Hoc|x86.Deploy.0 = Debug|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|Any CPU.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|Any CPU.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|Any CPU.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|ARM64.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|ARM64.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|ARM64.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhone.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhone.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhone.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhoneSimulator.ActiveCfg = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhoneSimulator.Build.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|iPhoneSimulator.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x64.ActiveCfg = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x64.Build.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x64.Deploy.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x86.ActiveCfg = Debug|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x86.Build.0 = Debug|x86 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.AppStore|x86.Deploy.0 = Debug|x86 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|Any CPU.ActiveCfg = Debug|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|ARM64.ActiveCfg = Debug|arm64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|ARM64.Build.0 = Debug|arm64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|ARM64.Deploy.0 = Debug|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhone.ActiveCfg = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhone.Build.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhone.Deploy.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhoneSimulator.ActiveCfg = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhoneSimulator.Build.0 = Debug|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|iPhoneSimulator.Deploy.0 = Debug|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|x64.ActiveCfg = Debug|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|x64.Build.0 = Debug|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Debug|x64.Deploy.0 = Debug|x64 @@ -179,64 +75,16 @@ Global {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|ARM64.ActiveCfg = Release|arm64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|ARM64.Build.0 = Release|arm64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|ARM64.Deploy.0 = Release|arm64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhone.ActiveCfg = Release|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhone.Build.0 = Release|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhone.Deploy.0 = Release|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhoneSimulator.ActiveCfg = Release|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhoneSimulator.Build.0 = Release|x64 - {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|iPhoneSimulator.Deploy.0 = Release|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x64.ActiveCfg = Release|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x64.Build.0 = Release|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x64.Deploy.0 = Release|x64 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x86.ActiveCfg = Release|x86 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x86.Build.0 = Release|x86 {BA503BD0-660A-4EA6-AB0E-11823F9BAD96}.Release|x86.Deploy.0 = Release|x86 - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|Any CPU.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|ARM64.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhone.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x64.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Ad-Hoc|x86.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|Any CPU.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|ARM64.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|ARM64.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|ARM64.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhone.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|iPhoneSimulator.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x64.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x64.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x64.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x86.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x86.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.AppStore|x86.Deploy.0 = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhone.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhone.Deploy.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|x64.ActiveCfg = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Debug|x86.ActiveCfg = Debug|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -245,12 +93,6 @@ Global {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|ARM64.ActiveCfg = Release|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|ARM64.Build.0 = Release|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|ARM64.Deploy.0 = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhone.ActiveCfg = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhone.Build.0 = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhone.Deploy.0 = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|x64.ActiveCfg = Release|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|x64.Build.0 = Release|Any CPU {7F7B3255-9EA0-4296-B690-14BE9E2204F3}.Release|x64.Deploy.0 = Release|Any CPU diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000000..9bba7baacf --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,36 @@ + + + true + 200.2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MAUI/Maui.Samples/ArcGIS.Samples.Maui.csproj b/src/MAUI/Maui.Samples/ArcGIS.Samples.Maui.csproj index a6489d8a92..ef8cf2a6d7 100644 --- a/src/MAUI/Maui.Samples/ArcGIS.Samples.Maui.csproj +++ b/src/MAUI/Maui.Samples/ArcGIS.Samples.Maui.csproj @@ -128,24 +128,22 @@ - - - - - - - + + + + + + + - - + + - - 1.5.1.1 - + diff --git a/src/MAUI/Maui.Samples/Converters/ItemToImageSourceConverter.cs b/src/MAUI/Maui.Samples/Converters/ItemToImageSourceConverter.cs deleted file mode 100644 index bfef3eb382..0000000000 --- a/src/MAUI/Maui.Samples/Converters/ItemToImageSourceConverter.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Esri.ArcGISRuntime; -using Esri.ArcGISRuntime.Portal; -using System.Globalization; - -namespace ArcGIS.Converters -{ - internal class ItemToImageSourceConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - Item mapItem = value as Item; - if (mapItem != null) - { - if (mapItem.ThumbnailUri != null) - { - // Sometimes image URIs have a . appended to them... - return ImageSource.FromUri(new Uri(mapItem.ThumbnailUri.OriginalString.TrimEnd('.'))); - } - - if (mapItem.Thumbnail != null && - mapItem.Thumbnail.LoadStatus == LoadStatus.Loaded && - mapItem.Thumbnail.Width > 0) - { - return ImageSource.FromStream(() => mapItem.Thumbnail.GetEncodedBufferAsync().Result); - } - } - - return null; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/MAUI/Maui.Samples/Helpers/SampleLoader.cs b/src/MAUI/Maui.Samples/Helpers/SampleLoader.cs index b1a025fff4..b372d85963 100644 --- a/src/MAUI/Maui.Samples/Helpers/SampleLoader.cs +++ b/src/MAUI/Maui.Samples/Helpers/SampleLoader.cs @@ -50,7 +50,7 @@ public static async Task LoadSample(SampleInfo sampleInfo) AuthenticationManager.Current.ChallengeHandler = null; // Load offline data before showing the sample. - if (sampleInfo.OfflineDataItems != null) + if (sampleInfo.OfflineDataItems != null && !await DataManager.HasSampleDataPresent(sampleInfo)) { CancellationTokenSource cancellationSource = new CancellationTokenSource(); diff --git a/src/MAUI/Maui.Samples/MauiProgram.cs b/src/MAUI/Maui.Samples/MauiProgram.cs index 58dfb74dc1..a968af007c 100644 --- a/src/MAUI/Maui.Samples/MauiProgram.cs +++ b/src/MAUI/Maui.Samples/MauiProgram.cs @@ -1,12 +1,15 @@ namespace ArcGIS.Samples.Maui; -using Esri.ArcGISRuntime.Maui; using CommunityToolkit.Maui; +using Esri.ArcGISRuntime.Maui; public static class MauiProgram { public static MauiApp CreateMauiApp() { +#if __ANDROID__ + Esri.ArcGISRuntime.UI.Controls.SceneView.MemoryLimit = 2 * 1073741824L; // 2Gb +#endif var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() diff --git a/src/MAUI/Maui.Samples/SamplePage.xaml b/src/MAUI/Maui.Samples/SamplePage.xaml index 770148f828..bd8f1e28a8 100644 --- a/src/MAUI/Maui.Samples/SamplePage.xaml +++ b/src/MAUI/Maui.Samples/SamplePage.xaml @@ -19,7 +19,6 @@ - @@ -34,7 +33,7 @@ Text="" VerticalOptions="Center" /> - + SourceFiles { get; } = new ObservableCollection(); @@ -41,13 +45,6 @@ public SamplePage() public SamplePage(ContentPage sample, SampleInfo sampleInfo) : this() { - this.NavigatedFrom += NavigatedFromEvent; - -#if IOS || MACCATALYST - // iOS / MacCatalyst lifecycle works differently, so we need to use the main page changing instead of the NavigatedFrom event for this. - Application.Current.MainPage.PropertyChanged += MainPagePropertyChanged; -#endif - // Set the sample variable. _sample = sample; @@ -83,6 +80,50 @@ public SamplePage(ContentPage sample, SampleInfo sampleInfo) : this() LoadSampleData(sampleInfo); } + protected override void OnNavigatedTo(NavigatedToEventArgs args) + { + base.OnNavigatedTo(args); + SampleDetailPage.Content = DescriptionView; + SourceCodeViewContainer.Content = SourceCodeView; + DescriptionView.Navigating += Webview_Navigating; + SourceCodeView.Navigating += Webview_Navigating; + } + + protected override void OnNavigatingFrom(NavigatingFromEventArgs args) + { + SampleDetailPage.Content = null; + SourceCodeViewContainer.Content = null; + DescriptionView.Navigating -= Webview_Navigating; + SourceCodeView.Navigating -= Webview_Navigating; + base.OnNavigatingFrom(args); + } + + protected override void OnNavigatedFrom(NavigatedFromEventArgs args) + { + base.OnNavigatedFrom(args); + + // Check that the navigation is backward from the sample and not forward into another page in the sample. + if (!Application.Current.MainPage.Navigation.NavigationStack.OfType().Any()) + { + // Explicit cleanup of the Map and SceneView instead of waiting for garbage collector can help when + // lots of geoviews are being opened and closed + foreach (var geoView in TreeWalker(_sample)) + { + if (geoView is MapView mapView) + { + mapView.Map = null; + if (mapView.LocationDisplay != null) mapView.LocationDisplay.IsEnabled = false; + } + else if (geoView is SceneView sceneView) sceneView.Scene = null; + + geoView.Handler?.DisconnectHandler(); + } + + if (_sample is IDisposable disposableSample) disposableSample.Dispose(); + if (_sample is IARSample ARSample) ARSample.StopAugmentedReality(); + } + } + private async void LoadSampleData(SampleInfo sampleInfo) { // Set up the description page. @@ -93,7 +134,6 @@ private async void LoadSampleData(SampleInfo sampleInfo) { Html = htmlString }; - DescriptionView.Navigating += Webview_Navigating; } catch (Exception ex) { @@ -104,7 +144,6 @@ private async void LoadSampleData(SampleInfo sampleInfo) { LoadSourceCode(sampleInfo); - SourceCodeView.Navigating += Webview_Navigating; } catch (Exception ex) { @@ -112,36 +151,6 @@ private async void LoadSampleData(SampleInfo sampleInfo) } } - private void MainPagePropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "CurrentPage") - { - TryDispose(); - } - } - - private void NavigatedFromEvent(object sender, NavigatedFromEventArgs e) - { - TryDispose(); - } - - private void TryDispose() - { - // Check that the navigation is backward from the sample and not forward into another page in the sample. - if (!Application.Current.MainPage.Navigation.NavigationStack.OfType().Any()) - { - // Explicit cleanup of the Map and SceneView instead of waiting for garbage collector can help when - // lots of geoviews are being opened and closed - foreach (var geoview in TreeWalker(_sample)) - { - geoview.Handler?.DisconnectHandler(); - } - - if (_sample is IDisposable disposableSample) disposableSample.Dispose(); - if (_sample is IARSample ARSample) ARSample.StopAugmentedReality(); - } - } - private static IEnumerable TreeWalker(VisualElement root) { if (root is not null) diff --git a/src/MAUI/Maui.Samples/Samples/Analysis/DistanceMeasurement/DistanceMeasurement.xaml b/src/MAUI/Maui.Samples/Samples/Analysis/DistanceMeasurement/DistanceMeasurement.xaml index 6fc7ca4dd2..95efe73be1 100644 --- a/src/MAUI/Maui.Samples/Samples/Analysis/DistanceMeasurement/DistanceMeasurement.xaml +++ b/src/MAUI/Maui.Samples/Samples/Analysis/DistanceMeasurement/DistanceMeasurement.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Analysis/LineOfSightGeoElement/LineOfSightGeoElement.xaml b/src/MAUI/Maui.Samples/Samples/Analysis/LineOfSightGeoElement/LineOfSightGeoElement.xaml index fac02c3962..890eef999d 100644 --- a/src/MAUI/Maui.Samples/Samples/Analysis/LineOfSightGeoElement/LineOfSightGeoElement.xaml +++ b/src/MAUI/Maui.Samples/Samples/Analysis/LineOfSightGeoElement/LineOfSightGeoElement.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui" xmlns:sampleViewer="clr-namespace:ArcGIS.Resources"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Analysis/QueryFeatureCountAndExtent/QueryFeatureCountAndExtent.xaml b/src/MAUI/Maui.Samples/Samples/Analysis/QueryFeatureCountAndExtent/QueryFeatureCountAndExtent.xaml index 7d43a752a1..021121e9cd 100644 --- a/src/MAUI/Maui.Samples/Samples/Analysis/QueryFeatureCountAndExtent/QueryFeatureCountAndExtent.xaml +++ b/src/MAUI/Maui.Samples/Samples/Analysis/QueryFeatureCountAndExtent/QueryFeatureCountAndExtent.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui" xmlns:sampleViewer="clr-namespace:ArcGIS.Resources"> - + - + - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/EditAndSyncFeatures/EditAndSyncFeatures.xaml b/src/MAUI/Maui.Samples/Samples/Data/EditAndSyncFeatures/EditAndSyncFeatures.xaml index 48c065020b..04211e619f 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/EditAndSyncFeatures/EditAndSyncFeatures.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/EditAndSyncFeatures/EditAndSyncFeatures.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/EditBranchVersioning/EditBranchVersioning.xaml b/src/MAUI/Maui.Samples/Samples/Data/EditBranchVersioning/EditBranchVersioning.xaml index 559a1d72fa..1797c2e5b1 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/EditBranchVersioning/EditBranchVersioning.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/EditBranchVersioning/EditBranchVersioning.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/EditFeatureAttachments/EditFeatureAttachments.xaml b/src/MAUI/Maui.Samples/Samples/Data/EditFeatureAttachments/EditFeatureAttachments.xaml index 085dadeb13..90a1aaded1 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/EditFeatureAttachments/EditFeatureAttachments.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/EditFeatureAttachments/EditFeatureAttachments.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/EditFeatureLinkedAnnotation/EditFeatureLinkedAnnotation.xaml b/src/MAUI/Maui.Samples/Samples/Data/EditFeatureLinkedAnnotation/EditFeatureLinkedAnnotation.xaml index 1b3b557205..3df6b5d947 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/EditFeatureLinkedAnnotation/EditFeatureLinkedAnnotation.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/EditFeatureLinkedAnnotation/EditFeatureLinkedAnnotation.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/FeatureLayerQuery/FeatureLayerQuery.xaml b/src/MAUI/Maui.Samples/Samples/Data/FeatureLayerQuery/FeatureLayerQuery.xaml index bccc0f7a25..c0cc113133 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/FeatureLayerQuery/FeatureLayerQuery.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/FeatureLayerQuery/FeatureLayerQuery.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/GenerateGeodatabaseReplica/GenerateGeodatabaseReplica.xaml b/src/MAUI/Maui.Samples/Samples/Data/GenerateGeodatabaseReplica/GenerateGeodatabaseReplica.xaml index 9984a96b38..1787216c83 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/GenerateGeodatabaseReplica/GenerateGeodatabaseReplica.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/GenerateGeodatabaseReplica/GenerateGeodatabaseReplica.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/GeodatabaseTransactions.xaml b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/GeodatabaseTransactions.xaml index ff9ea20e91..3f496a0c85 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/GeodatabaseTransactions.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/GeodatabaseTransactions.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + { - // Call a function (and await it) to get the local geodatabase (or generate it from the feature service) + // Call a function (and await it) to get the local geodatabase (or generate it from the feature service). await GetLocalGeodatabase(); - // Once the local geodatabase is available, load the tables as layers to the map + // Once the local geodatabase is available, load the tables as layers to the map. await LoadLocalGeodatabaseTables(); }; - // Create a new map with the oceans basemap and add it to the map view + // Create a new map with the oceans basemap and add it to the map view. Map map = new Map(BasemapStyle.ArcGISOceans); MyMapView.Map = map; } private async Task GetLocalGeodatabase() { - // Get the path to the local geodatabase for this platform (temp directory, for example) + // Get the path to the local geodatabase for this platform (temp directory, for example). string localGeodatabasePath = GetGdbPath(); try { - // See if the geodatabase file is already present + // See if the geodatabase file is already present. if (File.Exists(localGeodatabasePath)) { - // If the geodatabase is already available, open it, hide the progress control, and update the message + // If the geodatabase is already available, open it, hide the progress control, and update the message. _localGeodatabase = await Geodatabase.OpenAsync(localGeodatabasePath); LoadingProgressBar.IsVisible = false; MessageTextBlock.Text = "Using local geodatabase from '" + _localGeodatabase.Path + "'"; } else { - // Create a new GeodatabaseSyncTask with the uri of the feature server to pull from + // Create a new GeodatabaseSyncTask with the uri of the feature server to pull from. Uri uri = new Uri(SyncServiceUrl); GeodatabaseSyncTask gdbTask = await GeodatabaseSyncTask.CreateAsync(uri); - // Create parameters for the task: layers and extent to include, out spatial reference, and sync model + // Create parameters for the task: layers and extent to include, out spatial reference, and sync model. GenerateGeodatabaseParameters gdbParams = await gdbTask.CreateDefaultGenerateGeodatabaseParametersAsync(_extent); gdbParams.OutSpatialReference = MyMapView.SpatialReference; gdbParams.SyncModel = SyncModel.Layer; @@ -85,18 +89,18 @@ private async Task GetLocalGeodatabase() gdbParams.LayerOptions.Add(new GenerateLayerOption(0)); gdbParams.LayerOptions.Add(new GenerateLayerOption(1)); - // Create a geodatabase job that generates the geodatabase + // Create a geodatabase job that generates the geodatabase. GenerateGeodatabaseJob generateGdbJob = gdbTask.GenerateGeodatabase(gdbParams, localGeodatabasePath); - // Handle the job changed event and check the status of the job; store the geodatabase when it's ready + // Handle the job changed event and check the status of the job; store the geodatabase when it's ready. generateGdbJob.StatusChanged += (s, e) => { - // See if the job succeeded + // See if the job succeeded. if (generateGdbJob.Status == JobStatus.Succeeded) { Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => { - // Hide the progress control and update the message + // Hide the progress control and update the message. LoadingProgressBar.IsVisible = false; MessageTextBlock.Text = "Created local geodatabase"; }); @@ -105,20 +109,20 @@ private async Task GetLocalGeodatabase() { Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => { - // Hide the progress control and report the exception + // Hide the progress control and report the exception. LoadingProgressBar.IsVisible = false; MessageTextBlock.Text = "Unable to create local geodatabase: " + generateGdbJob.Error.Message; }); } }; - // Start the generate geodatabase job + // Start the generate geodatabase job. _localGeodatabase = await generateGdbJob.GetResultAsync(); } } catch (Exception ex) { - // Show a message for the exception encountered + // Show a message for the exception encountered. Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("Generate Geodatabase", "Unable to create offline database: " + ex.Message, "OK"); @@ -126,34 +130,34 @@ private async Task GetLocalGeodatabase() } } - // Function that loads the two point tables from the local geodatabase and displays them as feature layers + // Function that loads the two point tables from the local geodatabase and displays them as feature layers. private async Task LoadLocalGeodatabaseTables() { if (_localGeodatabase == null) { return; } - // Read the geodatabase tables and add them as layers + // Read the geodatabase tables and add them as layers. foreach (GeodatabaseFeatureTable table in _localGeodatabase.GeodatabaseFeatureTables) { try { - // Load the table so the TableName can be read + // Load the table so the TableName can be read. await table.LoadAsync(); - // Store a reference to the Birds table + // Store a reference to the Birds table. if (table.TableName.ToLower().Contains("birds")) { _birdTable = table; } - // Store a reference to the Marine table + // Store a reference to the Marine table. if (table.TableName.ToLower().Contains("marine")) { _marineTable = table; } - // Create a new feature layer to show the table in the map + // Create a new feature layer to show the table in the map. FeatureLayer layer = new FeatureLayer(table); - Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => MyMapView.Map.OperationalLayers.Add(layer)); + Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => MyMapView.Map?.OperationalLayers.Add(layer)); } catch (Exception e) { @@ -161,10 +165,10 @@ private async Task LoadLocalGeodatabaseTables() } } - // Handle the transaction status changed event + // Handle the transaction status changed event. _localGeodatabase.TransactionStatusChanged += GdbTransactionStatusChanged; - // Zoom the map view to the extent of the generated local datasets + // Zoom the map view to the extent of the generated local datasets. Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => { MyMapView.SetViewpoint(new Viewpoint(_marineTable.Extent)); @@ -175,15 +179,15 @@ private async Task LoadLocalGeodatabaseTables() private void GdbTransactionStatusChanged(object sender, TransactionStatusChangedEventArgs e) { - // Update UI controls based on whether the geodatabase has a current transaction + // Update UI controls based on whether the geodatabase has a current transaction. Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => { - // These buttons should be enabled when there IS a transaction + // These buttons should be enabled when there IS a transaction. AddBirdButton.IsEnabled = e.IsInTransaction; AddMarineButton.IsEnabled = e.IsInTransaction; StopEditingButton.IsEnabled = e.IsInTransaction; - // These buttons should be enabled when there is NOT a transaction + // These buttons should be enabled when there is NOT a transaction. StartEditingButton.IsEnabled = !e.IsInTransaction; SyncEditsButton.IsEnabled = !e.IsInTransaction; RequireTransactionCheckBox.IsEnabled = !e.IsInTransaction; @@ -192,83 +196,90 @@ private void GdbTransactionStatusChanged(object sender, TransactionStatusChanged private string GetGdbPath() { - // Set the platform-specific path for storing the geodatabase + // Set the platform-specific path for storing the geodatabase. string folder = string.Empty; #if WINDOWS folder = Windows.Storage.ApplicationData.Current.LocalFolder.Path; -#elif IOS || ANDROID +#elif IOS || ANDROID || MACCATALYST folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); #endif - // Set the final path + // Set the final path. return Path.Combine(folder, "savethebay.geodatabase"); } private void BeginTransaction(object sender, EventArgs e) { - // See if there is a transaction active for the geodatabase + // See if there is a transaction active for the geodatabase. if (!_localGeodatabase.IsInTransaction) { - // If not, begin a transaction + // If not, begin a transaction. _localGeodatabase.BeginTransaction(); MessageTextBlock.Text = "Transaction started"; } } - private async void AddNewFeature(object sender, EventArgs args) + private void AddNewFeature(object sender, EventArgs args) { - // See if it was the "Birds" or "Marine" button that was clicked + // See if it was the "Birds" or "Marine" button that was clicked. Button addFeatureButton = (Button)sender; try { - // Cancel execution of the sketch task if it is already active - if (MyMapView.SketchEditor.CancelCommand.CanExecute(null)) - { - MyMapView.SketchEditor.CancelCommand.Execute(null); - } - - // Store the correct table to edit (for the button clicked) - GeodatabaseFeatureTable editTable = null; + // Store the correct table to edit (for the button clicked). if (addFeatureButton == AddBirdButton) { - editTable = _birdTable; + _editTable = _birdTable; } else { - editTable = _marineTable; + _editTable = _marineTable; } - // Inform the user which table is being edited - MessageTextBlock.Text = "Click the map to add a new feature to the geodatabase table '" + editTable.TableName + "'"; + // Inform the user which table is being edited. + MessageTextBlock.Text = "Click the map to add a new feature to the geodatabase table '" + _editTable.TableName + "'"; - // Create a random value for the 'type' attribute (integer between 1 and 7) - Random random = new Random(DateTime.Now.Millisecond); - int featureType = random.Next(1, 7); + // Use the geometry editor to allow the user to draw a point on the map. + MyMapView.GeometryEditor.Start(GeometryType.Point); + + MyMapView.GeometryEditor.PropertyChanged += GeometryEditor_PropertyChanged; + } + catch (Exception ex) + { + // Report other exception messages. + MessageTextBlock.Text = ex.Message; + } + } - // Use the sketch editor to allow the user to draw a point on the map - MapPoint clickPoint = await MyMapView.SketchEditor.StartAsync(SketchCreationMode.Point, false) as MapPoint; + private async void GeometryEditor_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + // Check if the user finished drawing a point on the map. + if (e.PropertyName == nameof(GeometryEditor.Geometry)) + { + // Disconnect event handler to prevent multiple calls. + MyMapView.GeometryEditor.PropertyChanged -= GeometryEditor_PropertyChanged; - // Create a new feature (row) in the selected table - Feature newFeature = editTable.CreateFeature(); + // Get the active geometry. + Geometry geometry = MyMapView.GeometryEditor.Geometry; - // Set the geometry with the point the user clicked and the 'type' with the random integer - newFeature.Geometry = clickPoint; + // Create a new feature (row) in the selected table. + Feature newFeature = _editTable.CreateFeature(); + + // Create a random value for the 'type' attribute (integer between 1 and 7). + Random random = new Random(DateTime.Now.Millisecond); + int featureType = random.Next(1, 7); + + // Set the geometry with the point the user clicked and the 'type' with the random integer. + newFeature.Geometry = geometry; newFeature.SetAttributeValue("type", featureType); - // Add the new feature to the table - await editTable.AddFeatureAsync(newFeature); + // Add the new feature to the table. + await _editTable.AddFeatureAsync(newFeature); - // Clear the message - MessageTextBlock.Text = "New feature added to the '" + editTable.TableName + "' table"; - } - catch (TaskCanceledException) - { - // Ignore if the edit was canceled - } - catch (Exception ex) - { - // Report other exception messages - MessageTextBlock.Text = ex.Message; + // Set the newly created feature message. + MessageTextBlock.Text = "New feature added to the '" + _editTable.TableName + "' table"; + + // Stop the geometry editor, clearing the active geometry. + MyMapView.GeometryEditor.Stop(); } } @@ -276,32 +287,32 @@ private async void StopEditTransaction(object sender, EventArgs e) { try { - // Ask the user if they want to commit or rollback the transaction (or cancel to keep working in the transaction) + // Ask the user if they want to commit or rollback the transaction (or cancel to keep working in the transaction). string choice = await Application.Current.MainPage.DisplayActionSheet("Transaction", "Cancel", null, "Commit", "Rollback"); if (choice == "Commit") { - // See if there is a transaction active for the geodatabase + // See if there is a transaction active for the geodatabase. if (_localGeodatabase.IsInTransaction) { - // If there is, commit the transaction to store the edits (this will also end the transaction) + // If there is, commit the transaction to store the edits (this will also end the transaction). _localGeodatabase.CommitTransaction(); MessageTextBlock.Text = "Edits were committed to the local geodatabase."; } } else if (choice == "Rollback") { - // See if there is a transaction active for the geodatabase + // See if there is a transaction active for the geodatabase. if (_localGeodatabase.IsInTransaction) { - // If there is, rollback the transaction to discard the edits (this will also end the transaction) + // If there is, rollback the transaction to discard the edits (this will also end the transaction). _localGeodatabase.RollbackTransaction(); MessageTextBlock.Text = "Edits were rolled back and not stored to the local geodatabase."; } } else { - // For 'cancel' don't end the transaction with a commit or rollback + // For 'cancel' don't end the transaction with a commit or rollback. } } catch (Exception ex) @@ -310,16 +321,16 @@ private async void StopEditTransaction(object sender, EventArgs e) } } - // Change which controls are enabled if the user chooses to require/not require transactions for edits + // Change which controls are enabled if the user chooses to require/not require transactions for edits. private void RequireTransactionChanged(object sender, EventArgs e) { - // If the local geodatabase isn't created yet, return + // If the local geodatabase isn't created yet, return. if (_localGeodatabase == null) { return; } - // Get the value of the "require transactions" checkbox + // Get the value of the "require transactions" checkbox. bool mustHaveTransaction = RequireTransactionCheckBox.IsToggled; - // Warn the user if disabling transactions while a transaction is active + // Warn the user if disabling transactions while a transaction is active. if (!mustHaveTransaction && _localGeodatabase.IsInTransaction) { Application.Current.MainPage.DisplayAlert("Stop editing to end the current transaction.", "Current Transaction", "OK"); @@ -327,34 +338,34 @@ private void RequireTransactionChanged(object sender, EventArgs e) return; } - // Enable or disable controls according to the checkbox value + // Enable or disable controls according to the checkbox value. StartEditingButton.IsEnabled = mustHaveTransaction; StopEditingButton.IsEnabled = mustHaveTransaction && _localGeodatabase.IsInTransaction; AddBirdButton.IsEnabled = !mustHaveTransaction; AddMarineButton.IsEnabled = !mustHaveTransaction; } - // Synchronize edits in the local geodatabase with the service + // Synchronize edits in the local geodatabase with the service. public async void SynchronizeEdits(object sender, EventArgs e) { - // Show the progress bar while the sync is working + // Show the progress bar while the sync is working. LoadingProgressBar.IsVisible = true; try { - // Create a sync task with the URL of the feature service to sync + // Create a sync task with the URL of the feature service to sync. GeodatabaseSyncTask syncTask = await GeodatabaseSyncTask.CreateAsync(new Uri(SyncServiceUrl)); - // Create sync parameters + // Create sync parameters. SyncGeodatabaseParameters taskParameters = await syncTask.CreateDefaultSyncGeodatabaseParametersAsync(_localGeodatabase); - // Create a synchronize geodatabase job, pass in the parameters and the geodatabase + // Create a synchronize geodatabase job, pass in the parameters and the geodatabase. SyncGeodatabaseJob job = syncTask.SyncGeodatabase(taskParameters, _localGeodatabase); - // Handle the JobChanged event for the job + // Handle the JobChanged event for the job. job.StatusChanged += (s, arg) => { - // Report changes in the job status + // Report changes in the job status. if (job.Status == JobStatus.Succeeded) { // Report success ... @@ -372,17 +383,17 @@ public async void SynchronizeEdits(object sender, EventArgs e) } }; - // Await the completion of the job + // Await the completion of the job. await job.GetResultAsync(); } catch (Exception ex) { - // Show the message if an exception occurred + // Show the message if an exception occurred. MessageTextBlock.Text = "Error when synchronizing: " + ex.Message; } finally { - // Hide the progress bar when the sync job is complete + // Hide the progress bar when the sync job is complete. LoadingProgressBar.IsVisible = false; } } diff --git a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.md b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.md index f3c805d2ea..5ccf650ca7 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.md +++ b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.md @@ -27,6 +27,7 @@ When the sample loads, a feature service is taken offline as a geodatabase. When * Geodatabase.CommitTransaction * Geodatabase.IsInTransaction * Geodatabase.RollbackTransaction +* GeometryEditor ## About the data @@ -34,4 +35,4 @@ The sample uses a publicly-editable, sync-enabled [feature service](https://samp ## Tags -commit, database, geodatabase, transact, transactions \ No newline at end of file +commit, database, geodatabase, geometry editor, transact, transactions diff --git a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.metadata.json b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.metadata.json index 6bd8737def..07bc56e915 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.metadata.json +++ b/src/MAUI/Maui.Samples/Samples/Data/GeodatabaseTransactions/readme.metadata.json @@ -10,6 +10,7 @@ "commit", "database", "geodatabase", + "geometry editor", "transact", "transactions" ], @@ -22,7 +23,8 @@ "Geodatabase.BeginTransaction", "Geodatabase.CommitTransaction", "Geodatabase.IsInTransaction", - "Geodatabase.RollbackTransaction" + "Geodatabase.RollbackTransaction", + "GeometryEditor" ], "snippets": [ "GeodatabaseTransactions.xaml.cs", diff --git a/src/MAUI/Maui.Samples/Samples/Data/ListRelatedFeatures/ListRelatedFeatures.xaml b/src/MAUI/Maui.Samples/Samples/Data/ListRelatedFeatures/ListRelatedFeatures.xaml index 362cd332c5..0516576da2 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/ListRelatedFeatures/ListRelatedFeatures.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/ListRelatedFeatures/ListRelatedFeatures.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/ManageFeatures/ManageFeatures.xaml b/src/MAUI/Maui.Samples/Samples/Data/ManageFeatures/ManageFeatures.xaml index ba345be0d7..f6cf35dfd6 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/ManageFeatures/ManageFeatures.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/ManageFeatures/ManageFeatures.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/ReadShapefileMetadata/ReadShapefileMetadata.xaml b/src/MAUI/Maui.Samples/Samples/Data/ReadShapefileMetadata/ReadShapefileMetadata.xaml index b306e13510..19f54d5ba2 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/ReadShapefileMetadata/ReadShapefileMetadata.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/ReadShapefileMetadata/ReadShapefileMetadata.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/StatisticalQuery/StatisticalQuery.xaml b/src/MAUI/Maui.Samples/Samples/Data/StatisticalQuery/StatisticalQuery.xaml index 5b5d49e177..cdd8e456e1 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/StatisticalQuery/StatisticalQuery.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/StatisticalQuery/StatisticalQuery.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/SymbolizeShapefile/SymbolizeShapefile.xaml b/src/MAUI/Maui.Samples/Samples/Data/SymbolizeShapefile/SymbolizeShapefile.xaml index af57c9c57e..d751fdb912 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/SymbolizeShapefile/SymbolizeShapefile.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/SymbolizeShapefile/SymbolizeShapefile.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Data/ToggleBetweenFeatureRequestModes/ToggleBetweenFeatureRequestModes.xaml b/src/MAUI/Maui.Samples/Samples/Data/ToggleBetweenFeatureRequestModes/ToggleBetweenFeatureRequestModes.xaml index 779d9d142d..54f4839caa 100644 --- a/src/MAUI/Maui.Samples/Samples/Data/ToggleBetweenFeatureRequestModes/ToggleBetweenFeatureRequestModes.xaml +++ b/src/MAUI/Maui.Samples/Samples/Data/ToggleBetweenFeatureRequestModes/ToggleBetweenFeatureRequestModes.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Geometry/Buffer/Buffer.xaml b/src/MAUI/Maui.Samples/Samples/Geometry/Buffer/Buffer.xaml index 28e89b1f11..a1365b362c 100644 --- a/src/MAUI/Maui.Samples/Samples/Geometry/Buffer/Buffer.xaml +++ b/src/MAUI/Maui.Samples/Samples/Geometry/Buffer/Buffer.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui" xmlns:resources="clr-namespace:ArcGIS.Resources"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Geometry/BufferList/BufferList.xaml b/src/MAUI/Maui.Samples/Samples/Geometry/BufferList/BufferList.xaml index 9fe73aa043..3ca95a2007 100644 --- a/src/MAUI/Maui.Samples/Samples/Geometry/BufferList/BufferList.xaml +++ b/src/MAUI/Maui.Samples/Samples/Geometry/BufferList/BufferList.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + diff --git a/src/MAUI/Maui.Samples/Samples/Geometry/ConvexHull/ConvexHull.xaml b/src/MAUI/Maui.Samples/Samples/Geometry/ConvexHull/ConvexHull.xaml index 9f24e7310f..bf65ab6a80 100644 --- a/src/MAUI/Maui.Samples/Samples/Geometry/ConvexHull/ConvexHull.xaml +++ b/src/MAUI/Maui.Samples/Samples/Geometry/ConvexHull/ConvexHull.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"> - + - + diff --git a/src/MAUI/Maui.Samples/Samples/Geometry/DensifyAndGeneralize/DensifyAndGeneralize.xaml b/src/MAUI/Maui.Samples/Samples/Geometry/DensifyAndGeneralize/DensifyAndGeneralize.xaml index 971b6a9e24..1ff7fdce84 100644 --- a/src/MAUI/Maui.Samples/Samples/Geometry/DensifyAndGeneralize/DensifyAndGeneralize.xaml +++ b/src/MAUI/Maui.Samples/Samples/Geometry/DensifyAndGeneralize/DensifyAndGeneralize.xaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui" xmlns:sampleViewer="clr-namespace:ArcGIS.Resources"> - + - + - + - + - + - + diff --git a/src/MAUI/Maui.Samples/Samples/GraphicsOverlay/CreateAndEditGeometries/CreateAndEditGeometries.xaml b/src/MAUI/Maui.Samples/Samples/GraphicsOverlay/CreateAndEditGeometries/CreateAndEditGeometries.xaml new file mode 100644 index 0000000000..33986e68bf --- /dev/null +++ b/src/MAUI/Maui.Samples/Samples/GraphicsOverlay/CreateAndEditGeometries/CreateAndEditGeometries.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs deleted file mode 100644 index a6a0d28b14..0000000000 --- a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2017 Esri. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific -// language governing permissions and limitations under the License. - -using Esri.ArcGISRuntime.Data; -using Esri.ArcGISRuntime.Geometry; -using Esri.ArcGISRuntime.Mapping; -using Esri.ArcGISRuntime.Symbology; -using Esri.ArcGISRuntime.UI; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using Symbol = Esri.ArcGISRuntime.Symbology.Symbol; - -namespace ArcGIS.WPF.Samples.SketchOnMap -{ - [ArcGIS.Samples.Shared.Attributes.Sample( - name: "Sketch on map", - category: "GraphicsOverlay", - description: "Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map.", - instructions: "Choose which geometry type to sketch from one of the available buttons. Choose from points, multipoints, polylines, polygons, freehand polylines, freehand polygons, circles, ellipses, triangles, arrows and rectangles.", - tags: new[] { "draw", "edit" })] - public partial class SketchOnMap - { - // Graphics overlay to host sketch graphics. - private GraphicsOverlay _sketchOverlay; - - // Background colors for tool icons. - private static System.Windows.Media.SolidColorBrush LightGray; - private static System.Windows.Media.SolidColorBrush Red; - - // Button for keeping track of the currently enabled tool. - private static Button EnabledTool; - - private TaskCompletionSource _graphicCompletionSource; - - public SketchOnMap() - { - InitializeComponent(); - - // Call a function to set up the map and sketch editor. - Initialize(); - } - - private void Initialize() - { - // Create a map. - Map myMap = new Map(BasemapStyle.ArcGISImageryStandard); - - // Create graphics overlay to display sketch geometry. - _sketchOverlay = new GraphicsOverlay(); - MyMapView.GraphicsOverlays.Add(_sketchOverlay); - - // Assign the map to the MapView. - MyMapView.Map = myMap; - - // Set a viewpoint on the map view. - MyMapView.SetViewpoint(new Viewpoint(64.3286, -15.5314, 72223)); - - // Set the sketch editor as the page's data context. - DataContext = MyMapView.SketchEditor; - - // Ensure colors are consistent with XAML colors. - LightGray = System.Windows.Media.Brushes.LightGray; - Red = System.Windows.Media.Brushes.Red; - - // No tool currently selected, so simply instantiate the button. - EnabledTool = new Button(); - } - - #region Graphic and symbol helpers - - private Graphic SaveGraphic(Geometry geometry) - { - // Create a graphic to display the specified geometry. - Symbol symbol = null; - if (geometry != null) - { - switch (geometry.GeometryType) - { - // Symbolize with a fill symbol. - case GeometryType.Envelope: - case GeometryType.Polygon: - { - symbol = new SimpleFillSymbol() - { - Color = Color.Red, - Style = SimpleFillSymbolStyle.Solid - }; - break; - } - // Symbolize with a line symbol. - case GeometryType.Polyline: - { - symbol = new SimpleLineSymbol() - { - Color = Color.Red, - Style = SimpleLineSymbolStyle.Solid, - Width = 5d - }; - break; - } - // Symbolize with a marker symbol. - case GeometryType.Point: - case GeometryType.Multipoint: - { - symbol = new SimpleMarkerSymbol() - { - Color = Color.Red, - Style = SimpleMarkerSymbolStyle.Circle, - Size = 15d - }; - break; - } - } - - // Pass back a new graphic with the appropriate symbol. - return new Graphic(geometry, symbol); - } - - return null; - } - - private async Task GetGraphicAsync() - { - // Wait for the user to click a location on the map. - Geometry mapPoint = await MyMapView.SketchEditor.StartAsync(SketchCreationMode.Point, false); - - // Convert the map point to a screen point. - System.Windows.Point screenCoordinate = MyMapView.LocationToScreen((MapPoint)mapPoint); - - // Identify graphics in the graphics overlay using the point. - IReadOnlyList results = await MyMapView.IdentifyGraphicsOverlaysAsync(screenCoordinate, 2, false); - - // If results were found, get the first graphic. - Graphic graphic = null; - IdentifyGraphicsOverlayResult idResult = results.FirstOrDefault(); - if (idResult != null && idResult.Graphics.Count > 0) - { - graphic = idResult.Graphics.FirstOrDefault(); - } - - // Return the graphic (or null if none were found). - return graphic; - } - - #endregion Graphic and symbol helpers - - private void ShapeClick(object sender, RoutedEventArgs e) - { - // Update UI. - SelectTool(sender as Button); - - // Get the command parameter from the button press. - string mode = (sender as Button).CommandParameter.ToString(); - - // Check if the command parameter is defined in the SketchCreationMode enumerator. - if (Enum.IsDefined(typeof(SketchCreationMode), mode)) - { - _ = StarkSketch((SketchCreationMode)Enum.Parse(typeof(SketchCreationMode), mode)); - } - } - - private async Task StarkSketch(SketchCreationMode creationMode) - { - try - { - // Let the user draw on the map view using the chosen sketch mode. - Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true); - - // Create and add a graphic from the geometry the user drew. - Graphic graphic = SaveGraphic(geometry); - _sketchOverlay.Graphics.Add(graphic); - - // Enable/disable the clear and edit buttons according to whether or not graphics exist in the overlay. - ClearButton.IsEnabled = _sketchOverlay.Graphics.Count > 0; - EditButton.IsEnabled = _sketchOverlay.Graphics.Count > 0; - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel drawing. - } - catch (Exception ex) - { - // Report exceptions. - MessageBox.Show("Error drawing graphic shape: " + ex.Message, ex.GetType().Name, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void ClearButtonClick(object sender, RoutedEventArgs e) - { - // Remove all graphics from the graphics overlay. - _sketchOverlay.Graphics.Clear(); - - // Disable buttons that require graphics. - ClearButton.IsEnabled = false; - EditButton.IsEnabled = false; - } - - private async void EditButtonClick(object sender, RoutedEventArgs e) - { - try - { - // Update UI. - SelectTool(sender as Button); - - // Create a TaskCompletionSource object to wait for a graphic. - _graphicCompletionSource = new TaskCompletionSource(); - - // Wait for the user to select a graphic. - Graphic editGraphic = await _graphicCompletionSource.Task; - - // Let the user make changes to the graphic's geometry, await the result (updated geometry). - Geometry newGeometry = await MyMapView.SketchEditor.StartAsync(editGraphic.Geometry); - - // Display the updated geometry in the graphic. - editGraphic.Geometry = newGeometry; - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel editing. - } - catch (Exception ex) - { - // Report exceptions. - MessageBox.Show("Error editing shape: " + ex.Message, ex.GetType().Name, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - #region Tool selection UI helpers - - private void SelectTool(Button selectedButton) - { - // Gray out the background of the currently enabled tool. - if (EnabledTool != null) - EnabledTool.Background = LightGray; - - // Set the static variable to whichever button that was just clicked. - EnabledTool = selectedButton; - - // Set the background of the button to red. - EnabledTool.Background = Red; - } - - private void UnselectTool(object sender, RoutedEventArgs e) - { - // Gray out the background of the currently enabled tool. - if (EnabledTool != null) - EnabledTool.Background = LightGray; - - // Dereference the unselected tool's button. - EnabledTool = null; - } - - #endregion Tool selection UI helpers - - private async void OnGeoViewTapped(object sender, Esri.ArcGISRuntime.UI.Controls.GeoViewInputEventArgs e) - { - try - { - if (_graphicCompletionSource != null && !_graphicCompletionSource.Task.IsCompleted) - { - // Identify graphics in the graphics overlay using the point. - IReadOnlyList results = await MyMapView.IdentifyGraphicsOverlaysAsync(e.Position, 2, false); - - // If results were found, get the first graphic. - IdentifyGraphicsOverlayResult idResult = results.FirstOrDefault(); - if (idResult != null && idResult.Graphics.Count > 0) - { - Graphic graphic = idResult.Graphics.FirstOrDefault(); - _graphicCompletionSource.TrySetResult(graphic); - } - } - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel drawing. - } - catch (Exception ex) - { - // Report exceptions. - MessageBox.Show("Error editing shape: " + ex.Message, ex.GetType().Name, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } -} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md deleted file mode 100644 index 80b6785914..0000000000 --- a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -# Sketch on map - -Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map. - -![Image of sketch on map](SketchOnMap.jpg) - -## Use case - -A field worker could annotate features of interest on a map (via the GUI) such as location of dwellings (marked as points), geological features (polylines), or areas of glaciation (polygons). - -## How to use the sample - -Choose which geometry type to sketch from one of the available buttons. Choose from points, multipoints, polylines, polygons, freehand polylines, freehand polygons, circles, ellipses, triangles, arrows and rectangles. - -Use the control panel to cancel the sketch, undo or redo changes made to the sketch and to save the sketch to the graphics overlay. There is also the option to select a saved graphic and edit its geometry using the Sketch Editor. The graphics overlay can be cleared using the clear all button. - -## How it works - -1. Use `SketchEditor.StartAsync()` to start sketching. If editing an existing graphic's geometry, use `SketchEditor.StartAsync(graphic.Geometry)`. -2. Use the `UndoCommand` and `RedoCommand` to undo and redo edits in the sketch. -3. Use a `CompleteCommand` to finish the sketch and get the `Geometry` result. Use the `CancelCommand` to cancel the sketch. -4. Create a `Graphic` for the geometry and add it to the `GraphicsOverlay` in the map view. - -## Relevant API - -* Geometry -* Graphic -* GraphicsOverlay -* MapView -* SketchCreationMode -* SketchEditor - -## Tags - -draw, edit diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json deleted file mode 100644 index 477da68568..0000000000 --- a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "category": "GraphicsOverlay", - "description": "Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map.", - "formal_name": "SketchOnMap", - "ignore": false, - "images": [ - "SketchOnMap.jpg" - ], - "keywords": [ - "draw", - "edit" - ], - "offline_data": [], - "redirect_from": [ - "/net/latest/wpf/sample-code/sketchonmap.htm" - ], - "relevant_apis": [ - "Geometry", - "Graphic", - "GraphicsOverlay", - "MapView", - "SketchCreationMode", - "SketchEditor" - ], - "snippets": [ - "SketchOnMap.xaml.cs", - "SketchOnMap.xaml" - ], - "title": "Sketch on map" -} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png deleted file mode 100644 index d4ba3e9aa8..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png deleted file mode 100644 index 7613b0d9d8..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png deleted file mode 100644 index 6351f7d50e..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png deleted file mode 100644 index 336ff1e8a7..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png deleted file mode 100644 index 61be2582a8..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png deleted file mode 100644 index a92e248d14..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png deleted file mode 100644 index 36178ba15a..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png deleted file mode 100644 index c39fa03096..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png deleted file mode 100644 index 0ece5bfe47..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png deleted file mode 100644 index c80b705ea7..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png deleted file mode 100644 index 11b8233fa6..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png deleted file mode 100644 index 7438b1860d..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png deleted file mode 100644 index 8f812a4ab4..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png deleted file mode 100644 index a3f23f7d1d..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png deleted file mode 100644 index 9709d75693..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png deleted file mode 100644 index 74f88620bb..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png b/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png deleted file mode 100644 index ce572ce167..0000000000 Binary files a/src/WPF/WPF.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png and /dev/null differ diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg new file mode 100644 index 0000000000..a58dad1fc0 Binary files /dev/null and b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg differ diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml new file mode 100644 index 0000000000..415155a89c --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs new file mode 100644 index 0000000000..c9db7fd37c --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs @@ -0,0 +1,145 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using ArcGIS.Samples.Managers; +using Esri.ArcGISRuntime.ArcGISServices; +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Mapping; +using Esri.ArcGISRuntime.Mapping.Labeling; +using Esri.ArcGISRuntime.RealTime; +using Esri.ArcGISRuntime.Symbology; +using Esri.ArcGISRuntime.UI; +using Esri.ArcGISRuntime.UI.Controls; +using System; +using System.Linq; +using System.Text; +using System.Windows; + +namespace ArcGIS.WPF.Samples.AddCustomDynamicEntityDataSource +{ + [ArcGIS.Samples.Shared.Attributes.Sample( + name: "Add custom dynamic entity data source", + category: "Layers", + description: "Create a custom dynamic entity data source and display it using a dynamic entity layer.", + instructions: "Run the sample to view the map and the dynamic entity layer displaying the latest observation from the custom data source. Tap on a dynamic entity to view its attributes in a callout.", + tags: new[] { "data", "dynamic", "entity", "label", "labeling", "live", "real-time", "stream", "track" })] + [ArcGIS.Samples.Shared.Attributes.OfflineData("a8a942c228af4fac96baa78ad60f511f")] + public partial class AddCustomDynamicEntityDataSource + { + // Path to AIS Traffic Data json file. + private readonly string _localJsonFile = DataManager.GetDataFolder("a8a942c228af4fac96baa78ad60f511f", "AIS_MarineCadastre_SelectedVessels_CustomDataSource.json"); + + public AddCustomDynamicEntityDataSource() + { + InitializeComponent(); + Initialize(); + } + + private void Initialize() + { + // Create a new map with the navigation basemap style. + MyMapView.Map = new Map(BasemapStyle.ArcGISOceans); + + // Set the initial viewpoint. + MyMapView.SetViewpoint(new Viewpoint(47.984, -123.657, 3e6)); + + // Create a new custom file source. + // This takes the path to the simulation file, field name that will be used as the entity id, and the delay between each observation that is processed. + // In this example we are using a json file as our custom data source. + // This field value should be a unique identifier for each entity. + // Adjusting the value for the delay will change the speed at which the entities and their observations are displayed. + var customSource = new SimulatedDataSource(_localJsonFile, "MMSI", TimeSpan.FromMilliseconds(10)); + + // Create the dynamic entity layer using the custom data source. + var dynamicEntityLayer = new DynamicEntityLayer(customSource); + + // Set up the track display properties. + SetupTrackDisplayProperties(dynamicEntityLayer); + + // Set up the dynamic entity labeling. + SetupLabeling(dynamicEntityLayer); + + // Add the dynamic entity layer to the map. + MyMapView.Map.OperationalLayers.Add(dynamicEntityLayer); + } + + private void SetupTrackDisplayProperties(DynamicEntityLayer layer) + { + // Set up the track display properties, these properties will be used to configure the appearance of the track line and previous observations. + layer.TrackDisplayProperties.ShowPreviousObservations = true; + layer.TrackDisplayProperties.ShowTrackLine = true; + layer.TrackDisplayProperties.MaximumObservations = 20; + } + + private void SetupLabeling(DynamicEntityLayer layer) + { + // Define the label expression to be used, in this case we will use the "VesselName" for each of the dynamic entities. + var simpleLabelExpression = new SimpleLabelExpression("[VesselName]"); + + // Set the text symbol color and size for the labels. + var labelSymbol = new TextSymbol() { Color = System.Drawing.Color.Red, Size = 12d }; + + // Set the label position. + var labelDef = new LabelDefinition(simpleLabelExpression, labelSymbol) { Placement = LabelingPlacement.PointAboveCenter }; + + // Add the label definition to the dynamic entity layer and enable labels. + layer.LabelDefinitions.Add(labelDef); + layer.LabelsEnabled = true; + } + + private async void GeoViewTapped(object sender, GeoViewInputEventArgs e) + { + e.Handled = true; + try + { + MyMapView.DismissCallout(); + + // If no dynamic entity layer is present in the map, return. + var layer = MyMapView.Map?.OperationalLayers.OfType().FirstOrDefault(); + if (layer is null) return; + + // Identify the tapped observation. + IdentifyLayerResult results = await MyMapView.IdentifyLayerAsync(layer, e.Position, 2d, false); + DynamicEntityObservation observation = results.GeoElements.FirstOrDefault() as DynamicEntityObservation; + if (observation is null) return; + + // Get the dynamic entity from the observation. + var dynamicEntity = observation.GetDynamicEntity(); + if (dynamicEntity is null) return; + + // Build a string for observation attributes. + var stringBuilder = new StringBuilder(); + foreach (var attribute in new string[] { "VesselName", "CallSign", "COG", "SOG" }) + { + var value = dynamicEntity.Attributes[attribute].ToString(); + + // Account for when an attribue has an empty value. + if (!string.IsNullOrEmpty(value)) + { + stringBuilder.AppendLine(attribute + ": " + value); + } + } + + // The standard callout takes care of moving when the dynamic entity changes. + var calloutDef = new CalloutDefinition(stringBuilder.ToString().TrimEnd()); + if (layer.Renderer?.GetSymbol(dynamicEntity) is Symbol symbol) + { + await calloutDef.SetIconFromSymbolAsync(symbol); + } + + // Show the callout for the tapped dynamic entity. + MyMapView.ShowCalloutForGeoElement(dynamicEntity, e.Position, calloutDef); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), "Error identifying dynamic entity."); + } + } + } +} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs new file mode 100644 index 0000000000..b2d786f547 --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs @@ -0,0 +1,200 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Geometry; +using Esri.ArcGISRuntime.RealTime; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ArcGIS.WPF.Samples.AddCustomDynamicEntityDataSource +{ + public class SimulatedDataSource : DynamicEntityDataSource + { + // Hold a reference to the file stream reader, the process task, and the cancellation token source. + private Task _processTask; + private StreamReader _streamReader; + private CancellationTokenSource _cancellationTokenSource; + private List _fields; + + public SimulatedDataSource(string filePath, string entityIdField, TimeSpan delay) + { + FilePath = filePath; + EntityIdField = entityIdField; + Delay = delay; + } + + #region Properties + + // Expose the file path, entity ID field, and delay length as properties. + public string FilePath { get; } + public string EntityIdField { get; } + public TimeSpan Delay { get; } + + #endregion + + protected override async Task OnLoadAsync() + { + // Derive schema from the first row in the custom data source. + _fields = GetSchema(); + + // Open the file for processing. + Stream stream = File.OpenRead(FilePath); + _streamReader = new StreamReader(stream); + + // Create a new DynamicEntityDataSourceInfo using the entity ID field and the fields derived from the attributes of each observation in the custom data source. + return new DynamicEntityDataSourceInfo(EntityIdField, _fields) { SpatialReference = SpatialReferences.Wgs84 }; + } + + protected override Task OnConnectAsync(CancellationToken cancellationToken) + { + // On connecting to the custom data source begin processing the file. + _cancellationTokenSource = new CancellationTokenSource(); + _processTask = Task.Run(() => ObservationProcessLoopAsync(), _cancellationTokenSource.Token); + return Task.CompletedTask; + } + + protected override async Task OnDisconnectAsync() + { + // On disconnecting from the custom data source, stop processing the file. + _cancellationTokenSource?.Cancel(); + + if (_processTask != null) await _processTask; + + _cancellationTokenSource = null; + _processTask = null; + } + + private async Task ObservationProcessLoopAsync() + { + try + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + // Process the next observation. + var processed = await ProcessNextObservation(); + + // If the end of the file has been reached, break out of the loop. + if (_streamReader.EndOfStream) break; + + // If the observation was not processed, continue to the next observation. + if (!processed) continue; + + // If there is no delay, yield to the UI thread otherwise delay for the specified amount of time. + if (Delay == TimeSpan.Zero) + { + await Task.Yield(); + } + else + { + await Task.Delay(Delay, _cancellationTokenSource.Token); + } + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + } + + private async Task ProcessNextObservation() + { + _ = _streamReader ?? throw new ArgumentNullException("File stream not available."); + + // Read the next observation. + var json = await _streamReader.ReadLineAsync(); + + // If there is no json to read or the schema is not available, return false. + if (string.IsNullOrEmpty(json) || _fields is null) return false; + + try + { + JsonElement jsonElement = JsonSerializer.Deserialize(json); + + // Create a new MapPoint from the x and y coordinates of the observation. + MapPoint point = null; + if (jsonElement.TryGetProperty("geometry", out JsonElement jsonGeometry)) + { + point = new MapPoint( + jsonGeometry.GetProperty("x").GetDouble(), + jsonGeometry.GetProperty("y").GetDouble(), + SpatialReferences.Wgs84); + } + + // Get the dictionary of attributes from the observation using the field names as keys. + Dictionary attributes = new Dictionary(); + if (jsonElement.TryGetProperty("attributes", out JsonElement jsonAttributes)) + { + foreach (var field in _fields) + { + if (jsonAttributes.TryGetProperty(field.Name, out JsonElement prop)) + { + object value = null; + if (prop.ValueKind != JsonValueKind.Null) + { + if (prop.ValueKind == JsonValueKind.Number && field.FieldType == FieldType.Float64) + { + value = prop.GetDouble(); + } + else if (prop.ValueKind == JsonValueKind.Number && field.FieldType == FieldType.Int32) + { + value = prop.GetInt32(); + } + else if (prop.ValueKind == JsonValueKind.String) + { + value = prop.GetString(); + } + } + attributes.Add(field.Name, value); + } + } + } + + // Add the observation to the custom data source. + AddObservation(point, attributes); + return true; + } + catch (Exception ex) + { + Debug.WriteLine($"{ex}"); + return false; + } + } + + private static List GetSchema() + { + // Return a list of fields matching the attributes of each observation in the custom data source. + return new List() + { + new Field(FieldType.Text, "MMSI", string.Empty, 256), + new Field(FieldType.Float64, "BaseDateTime", string.Empty, 8), + new Field(FieldType.Float64, "LAT", string.Empty, 8), + new Field(FieldType.Float64, "LONG", string.Empty, 8), + new Field(FieldType.Float64, "SOG", string.Empty, 8), + new Field(FieldType.Float64, "COG", string.Empty, 8), + new Field(FieldType.Float64, "Heading", string.Empty, 8), + new Field(FieldType.Text, "VesselName", string.Empty, 256), + new Field(FieldType.Text, "IMO", string.Empty, 256), + new Field(FieldType.Text, "CallSign", string.Empty, 256), + new Field(FieldType.Text, "VesselType", string.Empty, 256), + new Field(FieldType.Text, "Status", string.Empty, 256), + new Field(FieldType.Float64, "Length", string.Empty, 8), + new Field(FieldType.Float64, "Width", string.Empty, 8), + new Field(FieldType.Text, "Cargo", string.Empty, 256), + new Field(FieldType.Text, "globalid", string.Empty, 256) + }; + } + } +} diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md new file mode 100644 index 0000000000..ff1f9dc425 --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md @@ -0,0 +1,50 @@ +# Add custom dynamic entity data source + +Create a custom dynamic entity data source and display it using a dynamic entity layer. + +![Image of add custom dynamic entity data source](AddCustomDynamicEntityDataSource.jpg) + +## Use case + +Developers can create a custom `DynamicEntityDataSource` to be able to visualize data from a variety of different feeds as dynamic entities using a `DynamicEntityLayer`. An example of this is in a mobile situational awareness app, where a custom `DynamicEntityDataSource` can be used to connect to peer-to-peer feeds in order to visualize real-time location tracks from teammates in the field. + +## How to use the sample + +Run the sample to view the map and the dynamic entity layer displaying the latest observation from the custom data source. Tap on a dynamic entity to view its attributes in a callout. + +## How it works + +Configure the custom data source: + +1. Create a custom data source implementation of a `DynamicEntityDataSource`. +2. Override `OnLoadAsync()` to specify the `DynamicEntityDataSourceInfo` for a given unique entity ID field and a list of `Field` objects matching the fields in the data source. +3. Override `OnConnectAsync()` to begin processing observations from the custom data source. +4. Loop through the observations and deserialize each observation into a `MapPoint` object and a `Dictionary` containing the attributes. +5. Use `DynamicEntityDataSource.AddObservation(mapPoint, attributes)` to add each observation to the custom data source. + +Configure the map view: + +1. Create a `DynamicEntityLayer` using the custom data source implementation. +2. Update values in the layer's `TrackDisplayProperties` to customize the layer's appearance. +3. Set up the layer's `LabelDefinitions` to display labels for each dynamic entity. +4. Configure a `GeoViewTapped` event handler on the `MapView` to select a dynamic entity and display the entity's attributes in a callout. + +## Relevant API + +* DynamicEntity +* DynamicEntityDataSource +* DynamicEntityLayer +* LabelDefinition +* TrackDisplayProperties + +## About the data + +This sample uses a [.json file containing observations of marine vessels in the Pacific North West](https://www.arcgis.com/home/item.html?id=a8a942c228af4fac96baa78ad60f511f) hosted on ArcGIS Online. + +## Additional information + +In this sample, we iterate through features in a GeoJSON file to mimic messages coming from a real-time feed. You can create a custom dyamic entity data source to process any data that contains observations which can be translated into `MapPoint` objects with associated `Dictionary` attributes. + +## Tags + +data, dynamic, entity, label, labeling, live, real-time, stream, track diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json new file mode 100644 index 0000000000..1c2e183c59 --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json @@ -0,0 +1,37 @@ +{ + "category": "Layers", + "description": "Create a custom dynamic entity data source and display it using a dynamic entity layer.", + "formal_name": "AddCustomDynamicEntityDataSource", + "ignore": false, + "images": [ + "AddCustomDynamicEntityDataSource.jpg" + ], + "keywords": [ + "data", + "dynamic", + "entity", + "label", + "labeling", + "live", + "real-time", + "stream", + "track" + ], + "offline_data": [], + "redirect_from": [ + "/net/latest/wpf/sample-code/addcustomdynamicentitydatasource.htm" + ], + "relevant_apis": [ + "DynamicEntity", + "DynamicEntityDataSource", + "DynamicEntityLayer", + "LabelDefinition", + "TrackDisplayProperties" + ], + "snippets": [ + "SimulatedDataSource.cs", + "AddCustomDynamicEntityDataSource.xaml.cs", + "AddCustomDynamicEntityDataSource.xaml" + ], + "title": "Add custom dynamic entity data source" +} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml index e5a3314417..7f4fff458b 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml @@ -11,14 +11,7 @@ FontWeight="SemiBold" Text="Select style:" TextWrapping="Wrap" /> - - - - - - - - + diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs index f2f2489d39..151d68fa09 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs @@ -8,14 +8,12 @@ // language governing permissions and limitations under the License. using ArcGIS.Samples.Managers; -using Esri.ArcGISRuntime.Geometry; using Esri.ArcGISRuntime.Mapping; using Esri.ArcGISRuntime.Portal; using Esri.ArcGISRuntime.Tasks.Offline; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -31,17 +29,15 @@ namespace ArcGIS.WPF.Samples.AddVectorTiledLayerFromCustomStyle [ArcGIS.Samples.Shared.Attributes.OfflineData("f4b742a57af344988b02227e2824ca5f")] public partial class AddVectorTiledLayerFromCustomStyle { - // ArcGIS Online portal item strings. - private readonly string[] _onlineItemIDs = + // ArcGIS Online vector tile layers. + private readonly string[] _portalItemIDs = { "1349bfa0ed08485d8a92c442a3850b06", "bd8ac41667014d98b933e97713ba8377", "02f85ec376084c508b9c8e5a311724fa", "1bf0cc4a4380468fbbff107e100f65a5", - }; - private readonly string[] _offlineItemIDs = - { + // Offline custom style vector tiled layer will be created once a VTPK is exported. "e01262ef2a4f4d91897d9bbd3a9b1075", "ce8a34e5d4ca4fa193a097511daa8855" }; @@ -70,22 +66,28 @@ private async Task Initialize() // Load the default portal. ArcGISPortal portal = await ArcGISPortal.CreateAsync(); - // Store a list of all portal items. - foreach (string item in _onlineItemIDs) - { - PortalItem portalItem = await PortalItem.CreateAsync(portal, item); - _vectorTiledLayers.Add(portalItem); - } - foreach (string item in _offlineItemIDs) + // Store a list all portal items. + foreach (string itemID in _portalItemIDs) { - PortalItem portalItem = await PortalItem.CreateAsync(portal, item); + PortalItem portalItem = await PortalItem.CreateAsync(portal, itemID); _vectorTiledLayers.Add(portalItem); } // Create a map using defaults. MyMapView.Map = new Map() { InitialViewpoint = _defaultViewpoint }; - // By default, the ComboBox will not reflect the default style. + // Populate the combo box. + StyleChooser.ItemsSource = new string[] + { + "Default", + "Style 1", + "Style 2", + "Style 3", + "Offline custom style - Light", + "Offline custom style - Dark" + }; + + // Select the default style. StyleChooser.SelectedIndex = 0; // Export offline custom styles. @@ -99,31 +101,12 @@ private async Task Initialize() } } - private void StyleChooserSelectionChanged(object sender, SelectionChangedEventArgs e) - { - _ = ChangeStyle(sender as ComboBox); - } - - private async Task ChangeStyle(ComboBox styleChooser) + private async void StyleChooser_SelectionChanged(object sender, SelectionChangedEventArgs e) { try { - int styleIndex = styleChooser.SelectedIndex; - - // Check if the user selected an online or offline custom style. - // Create a new basemap with the appropriate style. - if (_onlineItemIDs.Contains(_vectorTiledLayers[styleIndex].ItemId)) - { - MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(_vectorTiledLayers[styleIndex])); - await MyMapView.SetViewpointAsync(_defaultViewpoint); - } - else - { - // Determine which cache to use based on if the style selected is light (index 4) or dark. - ItemResourceCache cache = styleIndex == 4 ? _lightStyleResourceCache : _darkStyleResourceCache; - MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(new VectorTileCache(_localVectorPackagePath), cache)); - await MyMapView.SetViewpointAsync(_dodgeCityViewpoint); - } + // Get the style name and index of the selected item. + await ChangeStyleAsync(StyleChooser.SelectedIndex, StyleChooser.SelectedItem.ToString()); } catch (Exception ex) { @@ -132,6 +115,26 @@ private async Task ChangeStyle(ComboBox styleChooser) } } + private async Task ChangeStyleAsync(int styleIndex, string styleName) + { + // Check if the user selected a offline custom style. + // Create a new basemap with the appropriate style. + if (styleName.Contains("Offline")) + { + // Determine which cache to use based on if the style selected is light or dark. + ItemResourceCache cache = styleName.Contains("Light") ? _lightStyleResourceCache : _darkStyleResourceCache; + + MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(new VectorTileCache(_localVectorPackagePath), cache)); + await MyMapView.SetViewpointAsync(_dodgeCityViewpoint); + await cache.LoadAsync(); + } + else + { + MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(_vectorTiledLayers[styleIndex])); + await MyMapView.SetViewpointAsync(_defaultViewpoint); + } + } + private async Task ExportStyle(PortalItem vectorTiledLayer) { try @@ -140,13 +143,16 @@ private async Task ExportStyle(PortalItem vectorTiledLayer) ExportVectorTilesTask exportTask = await ExportVectorTilesTask.CreateAsync(vectorTiledLayer.Url); // Get the item resource path for the basemap styling. - string itemResourcePath = Path.Combine(Path.GetTempPath(), vectorTiledLayer.ItemId + "_styleItemResources"); + string itemResourceCachePath = Path.Combine(Path.GetTempPath(), vectorTiledLayer.ItemId + "_styleItemResources"); // If cache has been created previously, return. - if (Directory.Exists(itemResourcePath)) { return new ItemResourceCache(itemResourcePath); } + if (Directory.Exists(itemResourceCachePath) && (Directory.GetFiles(itemResourceCachePath).Length != 0)) + { + return new ItemResourceCache(itemResourceCachePath); + } // Create the export job and start it. - ExportVectorTilesJob job = exportTask.ExportStyleResourceCache(itemResourcePath); + ExportVectorTilesJob job = exportTask.ExportStyleResourceCache(itemResourceCachePath); job.Start(); // Wait for the job to complete. diff --git a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md index 7dd5e2a6e2..813c267976 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md +++ b/src/WPF/WPF.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md @@ -16,16 +16,15 @@ Pan and zoom to explore the vector tile basemap. 1. Create a `PortalItem` for each vector tiled layer. 2. Create a `Map` and set the default `Viewpoint`. -3. Update the `Basemap` and `Viewpoint` when a new style is selected. -4. Export the light and dark offline custom styles. - i. Create a `ExportVectorTilesTask` using the portal item. - ii. Get the path for where the cache is being stored locally. - iii. Return with the cache if the path already exists. - iv. Else, create a `ExportVectorTilesJob` by having the task call `ExportStyleResourceCache` with the path as a parameter. - v. Start the job. - vi. When the job completes, store the result as a `ExportVectorTilesResult`. - vii. Return the result's item resource cache. -5. Update the basemap upon a style selection change. +3. Export the light and dark offline custom styles. + i. Create a `ExportVectorTilesTask` using the portal item. + ii. Get the path for where the cache is being stored locally. + iii. Return with the cache if the path already exists. + iv. Else, create a `ExportVectorTilesJob` by having the task call `ExportStyleResourceCache` with the path as a parameter. + v. Start the job. + vi. When the job completes, store the result as a `ExportVectorTilesResult`. + vii. Return the result's item resource cache. +4. Update the `Basemap` and `Viewpoint` when a new style is selected. ## Relevant API diff --git a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs index 1594833d06..a0fdc3638b 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs +++ b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs @@ -10,10 +10,11 @@ using Esri.ArcGISRuntime.Geometry; using Esri.ArcGISRuntime.Mapping; using Esri.ArcGISRuntime.Ogc; -using Esri.ArcGISRuntime.UI; +using Esri.ArcGISRuntime.UI.Controls; using Microsoft.Win32; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Linq; using System.Windows; @@ -27,7 +28,7 @@ namespace ArcGIS.WPF.Samples.CreateAndSaveKmlFile category: "Layers", description: "Construct a KML document and save it as a KMZ file.", instructions: "Click on one of the buttons in the middle row to start adding a geometry. Click on the map view to place vertices. Click the \"Complete Sketch\" button to add the geometry to the KML document as a new KML placemark. Use the style interface to edit the style of the placemark. If you do not wish to set a style, click the \"Don't Apply Style\" button. When you are finished adding KML nodes, click on the \"Save KMZ file\" button to save the active KML document as a .kmz file on your system. Use the \"Reset\" button to clear the current KML document and start a new one.", - tags: new[] { "KML", "KMZ", "Keyhole", "OGC" })] + tags: new[] { "KML", "KMZ", "Keyhole", "OGC", "geometry editor" })] [ArcGIS.Samples.Shared.Attributes.OfflineData()] public partial class CreateAndSaveKmlFile { @@ -35,6 +36,7 @@ public partial class CreateAndSaveKmlFile private KmlDataset _kmlDataset; private KmlLayer _kmlLayer; private KmlPlacemark _currentPlacemark; + private GeometryType _geometryType; public CreateAndSaveKmlFile() { @@ -95,7 +97,7 @@ private void ResetKml() MyMapView.Map.OperationalLayers.Add(_kmlLayer); } - private async void Edit_Click(object sender, RoutedEventArgs e) + private void Edit_Click(object sender, RoutedEventArgs e) { try { @@ -104,26 +106,23 @@ private async void Edit_Click(object sender, RoutedEventArgs e) CompleteButton.Visibility = Visibility.Visible; SaveResetGrid.IsEnabled = false; - // Create variables for the sketch creation mode and color. - SketchCreationMode creationMode; - // Set the creation mode and UI based on which button called this method. switch (((Button)sender).Name) { case nameof(PointButton): - creationMode = SketchCreationMode.Point; + _geometryType = GeometryType.Point; InstructionsText.Text = "Tap to add a point."; StyleText.Text = "Select an icon for the placemark."; break; case nameof(PolylineButton): - creationMode = SketchCreationMode.Polyline; + _geometryType = GeometryType.Polyline; InstructionsText.Text = "Tap to add a vertex."; StyleText.Text = "Select a color for the placemark."; break; case nameof(PolygonButton): - creationMode = SketchCreationMode.Polygon; + _geometryType = GeometryType.Polygon; InstructionsText.Text = "Tap to add a vertex."; StyleText.Text = "Select a color for the placemark."; break; @@ -132,43 +131,13 @@ private async void Edit_Click(object sender, RoutedEventArgs e) return; } - // Get the user-drawn geometry. - Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true); - - // Project the geometry to WGS84 (WGS84 is required by the KML standard). - Geometry projectedGeometry = geometry.Project(SpatialReferences.Wgs84); - - // Create a KmlGeometry using the new geometry. - KmlGeometry kmlGeometry = new KmlGeometry(projectedGeometry, KmlAltitudeMode.ClampToGround); - - // Create a new placemark. - _currentPlacemark = new KmlPlacemark(kmlGeometry); - - // Add the placemark to the KmlDocument. - _kmlDocument.ChildNodes.Add(_currentPlacemark); - - // Enable the style editing UI. - StyleBorder.Visibility = Visibility.Visible; - MainUI.IsEnabled = false; - - // Choose whether to enable the icon picker or color picker. - IconPicker.Visibility = creationMode == SketchCreationMode.Point ? Visibility.Visible : Visibility.Collapsed; - ColorPicker.Visibility = creationMode != SketchCreationMode.Point ? Visibility.Visible : Visibility.Collapsed; + // Start the geometry editor. + MyMapView.GeometryEditor.Start(_geometryType); } catch (ArgumentException) { MessageBox.Show("Unsupported Geometry", "Error"); } - finally - { - // Reset the UI. - ShapesPanel.Visibility = Visibility.Visible; - CompleteButton.Visibility = Visibility.Collapsed; - InstructionsText.Text = "Select the type of feature you would like to add."; - - // Enable the save and reset buttons. - SaveResetGrid.IsEnabled = true; - } } private void Apply_Style_Click(object sender, RoutedEventArgs e) @@ -213,11 +182,60 @@ private void Complete_Click(object sender, RoutedEventArgs e) { try { - // Finish the sketch. - MyMapView.SketchEditor.CompleteCommand.Execute(null); + // Get the user-drawn geometry. + Geometry geometry = MyMapView.GeometryEditor.Stop(); + + // Hold a reference for the new placemark geometry. + KmlGeometry kmlGeometry; + + // Check to see if a geometry has been drawn. + if (!geometry.IsEmpty) + { + + if (MyMapView.SpatialReference != null && + geometry.SpatialReference != MyMapView.SpatialReference && + GeometryEngine.Project(geometry, MyMapView.SpatialReference) is Geometry projectedGeometry) + { + // Project the geometry to WGS84 (WGS84 is required by the KML standard). + projectedGeometry = geometry.Project(SpatialReferences.Wgs84); + + // Create a KmlGeometry using the projected geometry. + kmlGeometry = new KmlGeometry(projectedGeometry, KmlAltitudeMode.ClampToGround); + } + else + { + // Create a KmlGeometry using the user-drawn geometry. + kmlGeometry = new KmlGeometry(geometry, KmlAltitudeMode.ClampToGround); + } + + // Create a new placemark. + _currentPlacemark = new KmlPlacemark(kmlGeometry); + + // Add the placemark to the KmlDocument. + _kmlDocument.ChildNodes.Add(_currentPlacemark); + + // Enable the style editing UI. + StyleBorder.Visibility = Visibility.Visible; + MainUI.IsEnabled = false; + + // Choose whether to enable the icon picker or color picker. + IconPicker.Visibility = _geometryType == GeometryType.Point ? Visibility.Visible : Visibility.Collapsed; + ColorPicker.Visibility = _geometryType != GeometryType.Point ? Visibility.Visible : Visibility.Collapsed; + } } - catch (ArgumentException) + catch (Exception ex) { + Debug.WriteLine(ex.Message); + } + finally + { + // Reset the UI. + ShapesPanel.Visibility = Visibility.Visible; + CompleteButton.Visibility = Visibility.Collapsed; + InstructionsText.Text = "Select the type of feature you would like to add."; + + // Enable the save and reset buttons. + SaveResetGrid.IsEnabled = true; } } diff --git a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md index 11da843fc8..815c9a6b7d 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md +++ b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md @@ -17,7 +17,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click 1. Create a `KmlDocument` 2. Create a `KmlDataset` using the `KmlDocument`. 3. Create a `KmlLayer` using the `KmlDataset` and add it to `Map.OperationalLayers`. -4. Create `Geometry` using `SketchEditor`. +4. Create `Geometry` using `GeometryEditor`. 5. Project that `Geometry` to WGS84 using `GeometryEngine.Project`. 6. Create a `KmlGeometry` object using that projected `Geometry`. 7. Create a `KmlPlacemark` using the `KmlGeometry`. @@ -27,6 +27,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click ## Relevant API +* GeometryEditor * GeometryEngine.Project * KmlDataset * KmlDocument @@ -35,8 +36,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click * KmlNode.SaveAsASync * KmlPlacemark * KmlStyle -* SketchEditor ## Tags -Keyhole, KML, KMZ, OGC \ No newline at end of file +geometry editor, Keyhole, KML, KMZ, OGC diff --git a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json index 4f86d8b988..c43e08bc76 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json +++ b/src/WPF/WPF.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json @@ -10,13 +10,15 @@ "KML", "KMZ", "Keyhole", - "OGC" + "OGC", + "geometry editor" ], "offline_data": [], "redirect_from": [ "/net/latest/wpf/sample-code/createandsavekmlfile.htm" ], "relevant_apis": [ + "GeometryEditor", "GeometryEngine.Project", "KmlDataset", "KmlDocument", @@ -24,8 +26,7 @@ "KmlLayer", "KmlNode.SaveAsASync", "KmlPlacemark", - "KmlStyle", - "SketchEditor" + "KmlStyle" ], "snippets": [ "CreateAndSaveKmlFile.xaml.cs", diff --git a/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg new file mode 100644 index 0000000000..ce8036da8d Binary files /dev/null and b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg differ diff --git a/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml new file mode 100644 index 0000000000..e510deb085 --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs new file mode 100644 index 0000000000..0ebe6a1b2c --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs @@ -0,0 +1,81 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Mapping; +using Esri.ArcGISRuntime.Portal; +using Esri.ArcGISRuntime.UI.Controls; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace ArcGIS.WPF.Samples.DisplayPointsUsingClusteringFeatureReduction +{ + [ArcGIS.Samples.Shared.Attributes.Sample( + name: "Display points using clustering feature reduction", + category: "Layers", + description: "Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters.", + instructions: "Pan and zoom the map to view how clustering is dynamically updated. Toggle clustering off to view the original point features that make up the clustered elements. When clustering is on, you can click on a clustered geoelement to view aggregated information and summary statistics for that cluster. When clustering is toggled off and you click on the original feature you get access to information about individual power plant features.", + tags: new[] { "aggregate", "bin", "cluster", "group", "merge", "normalize", "reduce", "summarize" })] + [ArcGIS.Samples.Shared.Attributes.OfflineData()] + public partial class DisplayPointsUsingClusteringFeatureReduction + { + private FeatureLayer _layer; + + public DisplayPointsUsingClusteringFeatureReduction() + { + InitializeComponent(); + _ = Initialize(); + } + + private async Task Initialize() + { + // Get the power plants web map from the default portal. + var portal = await ArcGISPortal.CreateAsync(); + PortalItem portalItem = await PortalItem.CreateAsync(portal, "8916d50c44c746c1aafae001552bad23"); + + // Create a new map from the web map. + MyMapView.Map = new Map(portalItem); + + // Get the power plant feature layer once the map has finished loading. + await MyMapView.Map.LoadAsync(); + _layer = (FeatureLayer)MyMapView.Map.OperationalLayers.First(); + } + + private async void MyMapView_GeoViewTapped(object sender, GeoViewInputEventArgs e) + { + // Identify the tapped observation. + IdentifyLayerResult results = await MyMapView.IdentifyLayerAsync(_layer, e.Position, 3, true); + + // Return if no popups are found. + if (results.Popups.Count == 0) return; + + // Set the popup and make it visible. + PopupViewer.Popup = results.Popups.FirstOrDefault(); + PopupBackground.Visibility = Visibility.Visible; + } + + // Enable clustering feature reduction if the checkbox has been checked, disable otherwise. + private void CheckBox_CheckChanged(object sender, RoutedEventArgs e) + { + // This event is raised when sample is initially loaded when layer is null. + if (_layer == null) return; + + _layer.FeatureReduction.IsEnabled = (bool)(sender as CheckBox).IsChecked; + } + + // Hide and nullify the opened popup when user left clicks. + private void PopupBackground_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + PopupBackground.Visibility = Visibility.Collapsed; + PopupViewer.Popup = null; + } + } +} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md new file mode 100644 index 0000000000..78175dc49e --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md @@ -0,0 +1,38 @@ +# Display points using clustering feature reduction + +Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters. + +![](DisplayPointsUsingClusteringFeatureReduction.jpg) + +## Use case + +Feature clustering can be used to dynamically aggregate groups of points that are within proximity of each other in order to represent each group with a single symbol. Such grouping allows you to see patterns in the data that are difficult to visualize when a layer contains hundreds or thousands of points that overlap and cover each other. + +## How to use the sample + +Pan and zoom the map to view how clustering is dynamically updated. Toggle clustering off to view the original point features that make up the clustered elements. When clustering is on, you can click on a clustered geoelement to view aggregated information and summary statistics for that cluster. When clustering is toggled off and you click on the original feature you get access to information about individual power plant features. + +## How it works + +1. Create a map from a web map `PortalItem`. +2. Get the cluster enabled layer from the map's operational layers. +3. Get the `FeatureReduction` from the feature layer and set the `IsEnabled` bool to enable or disable clustering on the feature layer. +4. When the user clicks on the map, call `IdentifyFeatureLayerAsync()`, passing in the layer, map click location, tolerance, and `returnPopupsOnly` as true. +5. Set the `Popup` from the resulting `IdentifyLayerResult` to the `PopupViewer.Popup`. +6. Make the `PopupViewer` visible. + +## Relevant API + +* AggregateGeoElement +* FeatureLayer +* FeatureReduction +* GeoElement +* IdentifyLayerResult + +## About the data + +This sample uses a [web map](https://www.arcgis.com/home/item.html?id=8916d50c44c746c1aafae001552bad23) that displays the Esri [Global Power Plants](https://www.arcgis.com/home/item.html?id=eb54b44c65b846cca12914b87b315169) feature layer with feature reduction enabled. When enabled, the aggregate features symbology shows the color of the most common power plant type, and a size relative to the average plant capacity of the cluster. + +## Tags + +aggregate, bin, cluster, group, merge, normalize, reduce, summarize diff --git a/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json new file mode 100644 index 0000000000..c0528d8604 --- /dev/null +++ b/src/WPF/WPF.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json @@ -0,0 +1,35 @@ +{ + "category": "Layers", + "description": "Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters.", + "formal_name": "DisplayPointsUsingClusteringFeatureReduction", + "ignore": false, + "images": [ + "DisplayPointsUsingClusteringFeatureReduction.jpg" + ], + "keywords": [ + "aggregate", + "bin", + "cluster", + "group", + "merge", + "normalize", + "reduce", + "summarize" + ], + "offline_data": [], + "redirect_from": [ + "/net/latest/wpf/sample-code/displaypointsusingclusteringfeaturereduction.htm" + ], + "relevant_apis": [ + "AggregateGeoElement", + "FeatureLayer", + "FeatureReduction", + "GeoElement", + "IdentifyLayerResult" + ], + "snippets": [ + "DisplayPointsUsingClusteringFeatureReduction.xaml.cs", + "DisplayPointsUsingClusteringFeatureReduction.xaml" + ], + "title": "Display points using clustering feature reduction" +} \ No newline at end of file diff --git a/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg b/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg index f92184d831..01a40e4f4a 100644 Binary files a/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg and b/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg differ diff --git a/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml b/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml index f7f3851e9d..3cab5039a3 100644 --- a/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml +++ b/src/WPF/WPF.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml @@ -5,51 +5,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs deleted file mode 100644 index c9bf4f0d69..0000000000 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/SketchOnMap.xaml.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2017 Esri. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific -// language governing permissions and limitations under the License. - -using Esri.ArcGISRuntime.Data; -using Esri.ArcGISRuntime.Geometry; -using Esri.ArcGISRuntime.Mapping; -using Esri.ArcGISRuntime.Symbology; -using Esri.ArcGISRuntime.UI; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Threading.Tasks; - -namespace ArcGIS.WinUI.Samples.SketchOnMap -{ - [ArcGIS.Samples.Shared.Attributes.Sample( - name: "Sketch on map", - category: "GraphicsOverlay", - description: "Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map.", - instructions: "Choose which geometry type to sketch from one of the available buttons. Choose from points, multipoints, polylines, polygons, freehand polylines, freehand polygons, circles, ellipses, triangles, arrows and rectangles.", - tags: new[] { "draw", "edit" })] - public sealed partial class SketchOnMap - { - // Graphics overlay to host sketch graphics. - private GraphicsOverlay _sketchOverlay; - - // Background colors for tool icons. - private static Microsoft.UI.Xaml.Media.SolidColorBrush LightGray; - private static Microsoft.UI.Xaml.Media.SolidColorBrush Red; - - // Button for keeping track of the currently enabled tool. - private static Button EnabledTool; - - private TaskCompletionSource _graphicCompletionSource; - - public SketchOnMap() - { - InitializeComponent(); - - // Call a function to set up the map and sketch editor. - Initialize(); - } - - private void Initialize() - { - // Create a map. - Map myMap = new Map(BasemapStyle.ArcGISImageryStandard); - - // Create graphics overlay to display sketch geometry. - _sketchOverlay = new GraphicsOverlay(); - MyMapView.GraphicsOverlays.Add(_sketchOverlay); - - // Assign the map to the MapView. - MyMapView.Map = myMap; - - // Set a viewpoint on the map view. - MyMapView.SetViewpoint(new Viewpoint(64.3286, -15.5314, 72223)); - - // Set the sketch editor as the page's data context. - DataContext = MyMapView.SketchEditor; - - // Ensure colors are consistent with XAML colors. - LightGray = new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Color.FromArgb(255, 211, 211, 211)); - Red = new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 0)); - - // No tool currently selected, so simply instantiate the button. - EnabledTool = new Button(); - } - - #region Graphic and symbol helpers - - private Graphic SaveGraphic(Geometry geometry) - { - // Create a graphic to display the specified geometry. - Esri.ArcGISRuntime.Symbology.Symbol symbol = null; - if (geometry != null) - { - switch (geometry.GeometryType) - { - // Symbolize with a fill symbol. - case GeometryType.Envelope: - case GeometryType.Polygon: - { - symbol = new SimpleFillSymbol() - { - Color = Color.Red, - Style = SimpleFillSymbolStyle.Solid - }; - break; - } - // Symbolize with a line symbol. - case GeometryType.Polyline: - { - symbol = new SimpleLineSymbol() - { - Color = Color.Red, - Style = SimpleLineSymbolStyle.Solid, - Width = 5d - }; - break; - } - // Symbolize with a marker symbol. - case GeometryType.Point: - case GeometryType.Multipoint: - { - symbol = new SimpleMarkerSymbol() - { - Color = Color.Red, - Style = SimpleMarkerSymbolStyle.Circle, - Size = 15d - }; - break; - } - } - - // Pass back a new graphic with the appropriate symbol. - return new Graphic(geometry, symbol); - } - - return null; - } - - #endregion Graphic and symbol helpers - - private void ShapeClick(object sender, RoutedEventArgs e) - { - // Update UI. - SelectTool(sender as Button); - - // Get the command parameter from the button press. - string mode = (sender as Microsoft.UI.Xaml.Controls.Button).CommandParameter.ToString(); - - // Check if the command parameter is defined in the SketchCreationMode enumerator. - if (Enum.IsDefined(typeof(SketchCreationMode), mode)) - { - _ = StartSketch((SketchCreationMode)Enum.Parse(typeof(SketchCreationMode), mode)); - } - } - - private async Task StartSketch(SketchCreationMode creationMode) - { - try - { - // Let the user draw on the map view using the chosen sketch mode. - Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true); - - // Create and add a graphic from the geometry the user drew. - Graphic graphic = SaveGraphic(geometry); - _sketchOverlay.Graphics.Add(graphic); - - // Enable/disable the clear and edit buttons according to whether or not graphics exist in the overlay. - ClearButton.IsEnabled = _sketchOverlay.Graphics.Count > 0; - EditButton.IsEnabled = _sketchOverlay.Graphics.Count > 0; - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel drawing. - } - catch (Exception ex) - { - // Report exceptions. - await new MessageDialog2("Error drawing graphic shape: " + ex.Message, ex.GetType().Name).ShowAsync(); - } - } - - private void ClearButtonClick(object sender, RoutedEventArgs e) - { - // Remove all graphics from the graphics overlay. - _sketchOverlay.Graphics.Clear(); - - // Disable buttons that require graphics. - ClearButton.IsEnabled = false; - EditButton.IsEnabled = false; - } - - private async void EditButtonClick(object sender, RoutedEventArgs e) - { - try - { - // Update UI. - SelectTool(sender as Button); - - // Create a TaskCompletionSource object to wait for a graphic. - _graphicCompletionSource = new TaskCompletionSource(); - - // Wait for the user to select a graphic. - Graphic editGraphic = await _graphicCompletionSource.Task; - - // Let the user make changes to the graphic's geometry, await the result (updated geometry). - Geometry newGeometry = await MyMapView.SketchEditor.StartAsync(editGraphic.Geometry); - - // Display the updated geometry in the graphic. - editGraphic.Geometry = newGeometry; - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel editing. - } - catch (Exception ex) - { - // Report exceptions. - await new MessageDialog2("Error editing shape: " + ex.Message, ex.GetType().Name).ShowAsync(); - } - } - - #region Tool selection UI helpers - - private void SelectTool(Button selectedButton) - { - // Gray out the background of the currently selected tool. - if (EnabledTool is not null) - EnabledTool.Background = LightGray; - - // Set the static variable to whichever button that was just clicked. - EnabledTool = selectedButton; - - // Set the background of the currently selected tool to red. - EnabledTool.Background = Red; - } - - private void UnselectTool(object sender, RoutedEventArgs e) - { - // Gray out the background of the currently selected tool. - if (EnabledTool is not null) - EnabledTool.Background = LightGray; - - // Dereference the unselected tool's button. - EnabledTool = null; - } - - #endregion Tool selection UI helpers - - private async void OnGeoViewTapped(object sender, Esri.ArcGISRuntime.UI.Controls.GeoViewInputEventArgs e) - { - try - { - if (_graphicCompletionSource is not null && !_graphicCompletionSource.Task.IsCompleted) - { - // Identify graphics in the graphics overlay using the point. - IReadOnlyList results = await MyMapView.IdentifyGraphicsOverlaysAsync(e.Position, 2, false); - - // If results were found, get the first graphic. - IdentifyGraphicsOverlayResult idResult = results.FirstOrDefault(); - if (idResult != null && idResult.Graphics.Count > 0) - { - Graphic graphic = idResult.Graphics.FirstOrDefault(); - _graphicCompletionSource.TrySetResult(graphic); - } - } - } - catch (TaskCanceledException) - { - // Ignore ... let the user cancel drawing. - } - catch (Exception ex) - { - // Report exceptions. - await new MessageDialog2("Error editing shape: " + ex.Message, ex.GetType().Name).ShowAsync(); - } - } - } -} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md deleted file mode 100644 index 80b6785914..0000000000 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -# Sketch on map - -Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map. - -![Image of sketch on map](SketchOnMap.jpg) - -## Use case - -A field worker could annotate features of interest on a map (via the GUI) such as location of dwellings (marked as points), geological features (polylines), or areas of glaciation (polygons). - -## How to use the sample - -Choose which geometry type to sketch from one of the available buttons. Choose from points, multipoints, polylines, polygons, freehand polylines, freehand polygons, circles, ellipses, triangles, arrows and rectangles. - -Use the control panel to cancel the sketch, undo or redo changes made to the sketch and to save the sketch to the graphics overlay. There is also the option to select a saved graphic and edit its geometry using the Sketch Editor. The graphics overlay can be cleared using the clear all button. - -## How it works - -1. Use `SketchEditor.StartAsync()` to start sketching. If editing an existing graphic's geometry, use `SketchEditor.StartAsync(graphic.Geometry)`. -2. Use the `UndoCommand` and `RedoCommand` to undo and redo edits in the sketch. -3. Use a `CompleteCommand` to finish the sketch and get the `Geometry` result. Use the `CancelCommand` to cancel the sketch. -4. Create a `Graphic` for the geometry and add it to the `GraphicsOverlay` in the map view. - -## Relevant API - -* Geometry -* Graphic -* GraphicsOverlay -* MapView -* SketchCreationMode -* SketchEditor - -## Tags - -draw, edit diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json deleted file mode 100644 index fd438c2e99..0000000000 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/readme.metadata.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "category": "GraphicsOverlay", - "description": "Use the Sketch Editor to edit or sketch a new point, line, or polygon geometry on to a map.", - "formal_name": "SketchOnMap", - "ignore": false, - "images": [ - "SketchOnMap.jpg" - ], - "keywords": [ - "draw", - "edit" - ], - "offline_data": [], - "redirect_from": [ - "/net/latest/winui/sample-code/sketchonmap.htm" - ], - "relevant_apis": [ - "Geometry", - "Graphic", - "GraphicsOverlay", - "MapView", - "SketchCreationMode", - "SketchEditor" - ], - "snippets": [ - "SketchOnMap.xaml.cs", - "SketchOnMap.xaml" - ], - "title": "Sketch on map" -} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png deleted file mode 100644 index d4ba3e9aa8..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/arrow.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png deleted file mode 100644 index 7613b0d9d8..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/brush.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png deleted file mode 100644 index 6351f7d50e..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/circle.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png deleted file mode 100644 index 336ff1e8a7..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/clear.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png deleted file mode 100644 index 61be2582a8..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/edit.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png deleted file mode 100644 index a92e248d14..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/ellipse.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png deleted file mode 100644 index 36178ba15a..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/freehand-polyline.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png deleted file mode 100644 index c39fa03096..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/multipoint.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png deleted file mode 100644 index 0ece5bfe47..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/point.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png deleted file mode 100644 index c80b705ea7..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polygon.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png deleted file mode 100644 index 11b8233fa6..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/polyline.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png deleted file mode 100644 index 7438b1860d..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/rectangle.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png deleted file mode 100644 index 8f812a4ab4..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/redo.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png deleted file mode 100644 index a3f23f7d1d..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/save.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png deleted file mode 100644 index 9709d75693..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/trash-can-outline.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png deleted file mode 100644 index 74f88620bb..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/triangle.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png deleted file mode 100644 index ce572ce167..0000000000 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/GraphicsOverlay/SketchOnMap/resources/undo.png and /dev/null differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg new file mode 100644 index 0000000000..440281b7ab Binary files /dev/null and b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.jpg differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml new file mode 100644 index 0000000000..f11f56593c --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs new file mode 100644 index 0000000000..75d81a8584 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/AddCustomDynamicEntityDataSource.xaml.cs @@ -0,0 +1,144 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using ArcGIS.Samples.Managers; +using Esri.ArcGISRuntime.ArcGISServices; +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Mapping; +using Esri.ArcGISRuntime.Mapping.Labeling; +using Esri.ArcGISRuntime.RealTime; +using Esri.ArcGISRuntime.Symbology; +using Esri.ArcGISRuntime.UI; +using Esri.ArcGISRuntime.UI.Controls; +using System; +using System.Linq; +using System.Text; + +namespace ArcGIS.WinUI.Samples.AddCustomDynamicEntityDataSource +{ + [ArcGIS.Samples.Shared.Attributes.Sample( + name: "Add custom dynamic entity data source", + category: "Layers", + description: "Create a custom dynamic entity data source and display it using a dynamic entity layer.", + instructions: "Run the sample to view the map and the dynamic entity layer displaying the latest observation from the custom data source. Tap on a dynamic entity to view its attributes in a callout.", + tags: new[] { "data", "dynamic", "entity", "label", "labeling", "live", "real-time", "stream", "track" })] + [ArcGIS.Samples.Shared.Attributes.OfflineData("a8a942c228af4fac96baa78ad60f511f")] + public partial class AddCustomDynamicEntityDataSource + { + // Path to AIS Traffic Data json file. + private readonly string _localJsonFile = DataManager.GetDataFolder("a8a942c228af4fac96baa78ad60f511f", "AIS_MarineCadastre_SelectedVessels_CustomDataSource.json"); + + public AddCustomDynamicEntityDataSource() + { + InitializeComponent(); + Initialize(); + } + + private void Initialize() + { + // Create a new map with the navigation basemap style. + MyMapView.Map = new Map(BasemapStyle.ArcGISOceans); + + // Set the initial viewpoint. + MyMapView.SetViewpoint(new Viewpoint(47.984, -123.657, 3e6)); + + // Create a new custom file source. + // This takes the path to the simulation file, field name that will be used as the entity id, and the delay between each observation that is processed. + // In this example we are using a json file as our custom data source. + // This field value should be a unique identifier for each entity. + // Adjusting the value for the delay will change the speed at which the entities and their observations are displayed. + var customSource = new SimulatedDataSource(_localJsonFile, "MMSI", TimeSpan.FromMilliseconds(10)); + + // Create the dynamic entity layer using the custom data source. + var dynamicEntityLayer = new DynamicEntityLayer(customSource); + + // Set up the track display properties. + SetupTrackDisplayProperties(dynamicEntityLayer); + + // Set up the dynamic entity labeling. + SetupLabeling(dynamicEntityLayer); + + // Add the dynamic entity layer to the map. + MyMapView.Map.OperationalLayers.Add(dynamicEntityLayer); + } + + private void SetupTrackDisplayProperties(DynamicEntityLayer layer) + { + // Set up the track display properties, these properties will be used to configure the appearance of the track line and previous observations. + layer.TrackDisplayProperties.ShowPreviousObservations = true; + layer.TrackDisplayProperties.ShowTrackLine = true; + layer.TrackDisplayProperties.MaximumObservations = 20; + } + + private void SetupLabeling(DynamicEntityLayer layer) + { + // Define the label expression to be used, in this case we will use the "VesselName" for each of the dynamic entities. + var simpleLabelExpression = new SimpleLabelExpression("[VesselName]"); + + // Set the text symbol color and size for the labels. + var labelSymbol = new TextSymbol() { Color = System.Drawing.Color.Red, Size = 12d }; + + // Set the label position. + var labelDef = new LabelDefinition(simpleLabelExpression, labelSymbol) { Placement = LabelingPlacement.PointAboveCenter }; + + // Add the label definition to the dynamic entity layer and enable labels. + layer.LabelDefinitions.Add(labelDef); + layer.LabelsEnabled = true; + } + + private async void GeoViewTapped(object sender, GeoViewInputEventArgs e) + { + e.Handled = true; + try + { + MyMapView.DismissCallout(); + + // If no dynamic entity layer is present in the map, return. + var layer = MyMapView.Map?.OperationalLayers.OfType().FirstOrDefault(); + if (layer is null) return; + + // Identify the tapped observation. + IdentifyLayerResult results = await MyMapView.IdentifyLayerAsync(layer, e.Position, 2d, false); + DynamicEntityObservation observation = results.GeoElements.FirstOrDefault() as DynamicEntityObservation; + if (observation is null) return; + + // Get the dynamic entity from the observation. + var dynamicEntity = observation.GetDynamicEntity(); + if (dynamicEntity is null) return; + + // Build a string for observation attributes. + var stringBuilder = new StringBuilder(); + foreach (var attribute in new string[] { "VesselName", "CallSign", "COG", "SOG" }) + { + var value = dynamicEntity.Attributes[attribute].ToString(); + + // Account for when an attribue has an empty value. + if (!string.IsNullOrEmpty(value)) + { + stringBuilder.AppendLine(attribute + ": " + value); + } + } + + // The standard callout takes care of moving when the dynamic entity changes. + var calloutDef = new CalloutDefinition(stringBuilder.ToString().TrimEnd()); + if (layer.Renderer?.GetSymbol(dynamicEntity) is Symbol symbol) + { + await calloutDef.SetIconFromSymbolAsync(symbol); + } + + // Show the callout for the tapped dynamic entity. + MyMapView.ShowCalloutForGeoElement(dynamicEntity, e.Position, calloutDef); + } + catch (Exception ex) + { + await new MessageDialog2(ex.ToString(), "Error identifying dynamic entity.").ShowAsync(); + } + } + } +} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs new file mode 100644 index 0000000000..a00a89f2c9 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/SimulatedDataSource.cs @@ -0,0 +1,200 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Geometry; +using Esri.ArcGISRuntime.RealTime; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ArcGIS.WinUI.Samples.AddCustomDynamicEntityDataSource +{ + public class SimulatedDataSource : DynamicEntityDataSource + { + // Hold a reference to the file stream reader, the process task, and the cancellation token source. + private Task? _processTask; + private StreamReader? _streamReader; + private CancellationTokenSource? _cancellationTokenSource; + private List _fields; + + public SimulatedDataSource(string filePath, string entityIdField, TimeSpan delay) + { + FilePath = filePath; + EntityIdField = entityIdField; + Delay = delay; + } + + #region Properties + + // Expose the file path, entity ID field, and delay length as properties. + public string FilePath { get; } + public string EntityIdField { get; } + public TimeSpan Delay { get; } + + #endregion + + protected override async Task OnLoadAsync() + { + // Derive schema from the first row in the custom data source. + _fields = GetSchema(); + + // Open the file for processing. + Stream stream = File.OpenRead(FilePath); + _streamReader = new StreamReader(stream); + + // Create a new DynamicEntityDataSourceInfo using the entity ID field and the fields derived from the attributes of each observation in the custom data source. + return new DynamicEntityDataSourceInfo(EntityIdField, _fields) { SpatialReference = SpatialReferences.Wgs84 }; + } + + protected override Task OnConnectAsync(CancellationToken cancellationToken) + { + // On connecting to the custom data source begin processing the file. + _cancellationTokenSource = new(); + _processTask = Task.Run(() => ObservationProcessLoopAsync(), _cancellationTokenSource.Token); + return Task.CompletedTask; + } + + protected override async Task OnDisconnectAsync() + { + // On disconnecting from the custom data source, stop processing the file. + _cancellationTokenSource?.Cancel(); + + if (_processTask is not null) await _processTask; + + _cancellationTokenSource = null; + _processTask = null; + } + + private async Task ObservationProcessLoopAsync() + { + try + { + while (!_cancellationTokenSource!.IsCancellationRequested) + { + // Process the next observation. + var processed = await ProcessNextObservation(); + + // If the end of the file has been reached, break out of the loop. + if (_streamReader.EndOfStream) break; + + // If the observation was not processed, continue to the next observation. + if (!processed) continue; + + // If there is no delay, yield to the UI thread otherwise delay for the specified amount of time. + if (Delay == TimeSpan.Zero) + { + await Task.Yield(); + } + else + { + await Task.Delay(Delay, _cancellationTokenSource.Token); + } + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + } + + private async Task ProcessNextObservation() + { + _ = _streamReader ?? throw new ArgumentNullException("File stream not available."); + + // Read the next observation. + var json = await _streamReader.ReadLineAsync(); + + // If there is no json to read or the schema is not available, return false. + if (string.IsNullOrEmpty(json) || _fields is null) return false; + + try + { + JsonElement jsonElement = JsonSerializer.Deserialize(json); + + // Create a new MapPoint from the x and y coordinates of the observation. + MapPoint? point = null; + if (jsonElement.TryGetProperty("geometry", out JsonElement jsonGeometry)) + { + point = new MapPoint( + jsonGeometry.GetProperty("x").GetDouble(), + jsonGeometry.GetProperty("y").GetDouble(), + SpatialReferences.Wgs84); + } + + // Get the dictionary of attributes from the observation using the field names as keys. + Dictionary attributes = new(); + if (jsonElement.TryGetProperty("attributes", out JsonElement jsonAttributes)) + { + foreach (var field in _fields) + { + if (jsonAttributes.TryGetProperty(field.Name, out JsonElement prop)) + { + object? value = null; + if (prop.ValueKind != JsonValueKind.Null) + { + if (prop.ValueKind == JsonValueKind.Number && field.FieldType == FieldType.Float64) + { + value = prop.GetDouble(); + } + else if (prop.ValueKind == JsonValueKind.Number && field.FieldType == FieldType.Int32) + { + value = prop.GetInt32(); + } + else if (prop.ValueKind == JsonValueKind.String) + { + value = prop.GetString(); + } + } + attributes.Add(field.Name, value); + } + } + } + + // Add the observation to the custom data source. + AddObservation(point, attributes); + return true; + } + catch (Exception ex) + { + Debug.WriteLine($"{ex}"); + return false; + } + } + + private static List GetSchema() + { + // Return a list of fields matching the attributes of each observation in the custom data source. + return new List() + { + new Field(FieldType.Text, "MMSI", string.Empty, 256), + new Field(FieldType.Float64, "BaseDateTime", string.Empty, 8), + new Field(FieldType.Float64, "LAT", string.Empty, 8), + new Field(FieldType.Float64, "LONG", string.Empty, 8), + new Field(FieldType.Float64, "SOG", string.Empty, 8), + new Field(FieldType.Float64, "COG", string.Empty, 8), + new Field(FieldType.Float64, "Heading", string.Empty, 8), + new Field(FieldType.Text, "VesselName", string.Empty, 256), + new Field(FieldType.Text, "IMO", string.Empty, 256), + new Field(FieldType.Text, "CallSign", string.Empty, 256), + new Field(FieldType.Text, "VesselType", string.Empty, 256), + new Field(FieldType.Text, "Status", string.Empty, 256), + new Field(FieldType.Float64, "Length", string.Empty, 8), + new Field(FieldType.Float64, "Width", string.Empty, 8), + new Field(FieldType.Text, "Cargo", string.Empty, 256), + new Field(FieldType.Text, "globalid", string.Empty, 256) + }; + } + } +} diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md new file mode 100644 index 0000000000..ff1f9dc425 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.md @@ -0,0 +1,50 @@ +# Add custom dynamic entity data source + +Create a custom dynamic entity data source and display it using a dynamic entity layer. + +![Image of add custom dynamic entity data source](AddCustomDynamicEntityDataSource.jpg) + +## Use case + +Developers can create a custom `DynamicEntityDataSource` to be able to visualize data from a variety of different feeds as dynamic entities using a `DynamicEntityLayer`. An example of this is in a mobile situational awareness app, where a custom `DynamicEntityDataSource` can be used to connect to peer-to-peer feeds in order to visualize real-time location tracks from teammates in the field. + +## How to use the sample + +Run the sample to view the map and the dynamic entity layer displaying the latest observation from the custom data source. Tap on a dynamic entity to view its attributes in a callout. + +## How it works + +Configure the custom data source: + +1. Create a custom data source implementation of a `DynamicEntityDataSource`. +2. Override `OnLoadAsync()` to specify the `DynamicEntityDataSourceInfo` for a given unique entity ID field and a list of `Field` objects matching the fields in the data source. +3. Override `OnConnectAsync()` to begin processing observations from the custom data source. +4. Loop through the observations and deserialize each observation into a `MapPoint` object and a `Dictionary` containing the attributes. +5. Use `DynamicEntityDataSource.AddObservation(mapPoint, attributes)` to add each observation to the custom data source. + +Configure the map view: + +1. Create a `DynamicEntityLayer` using the custom data source implementation. +2. Update values in the layer's `TrackDisplayProperties` to customize the layer's appearance. +3. Set up the layer's `LabelDefinitions` to display labels for each dynamic entity. +4. Configure a `GeoViewTapped` event handler on the `MapView` to select a dynamic entity and display the entity's attributes in a callout. + +## Relevant API + +* DynamicEntity +* DynamicEntityDataSource +* DynamicEntityLayer +* LabelDefinition +* TrackDisplayProperties + +## About the data + +This sample uses a [.json file containing observations of marine vessels in the Pacific North West](https://www.arcgis.com/home/item.html?id=a8a942c228af4fac96baa78ad60f511f) hosted on ArcGIS Online. + +## Additional information + +In this sample, we iterate through features in a GeoJSON file to mimic messages coming from a real-time feed. You can create a custom dyamic entity data source to process any data that contains observations which can be translated into `MapPoint` objects with associated `Dictionary` attributes. + +## Tags + +data, dynamic, entity, label, labeling, live, real-time, stream, track diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json new file mode 100644 index 0000000000..f6a9d946e4 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddCustomDynamicEntityDataSource/readme.metadata.json @@ -0,0 +1,37 @@ +{ + "category": "Layers", + "description": "Create a custom dynamic entity data source and display it using a dynamic entity layer.", + "formal_name": "AddCustomDynamicEntityDataSource", + "ignore": false, + "images": [ + "AddCustomDynamicEntityDataSource.jpg" + ], + "keywords": [ + "data", + "dynamic", + "entity", + "label", + "labeling", + "live", + "real-time", + "stream", + "track" + ], + "offline_data": [], + "redirect_from": [ + "/net/latest/winui/sample-code/addcustomdynamicentitydatasource.htm" + ], + "relevant_apis": [ + "DynamicEntity", + "DynamicEntityDataSource", + "DynamicEntityLayer", + "LabelDefinition", + "TrackDisplayProperties" + ], + "snippets": [ + "SimulatedDataSource.cs", + "AddCustomDynamicEntityDataSource.xaml.cs", + "AddCustomDynamicEntityDataSource.xaml" + ], + "title": "Add custom dynamic entity data source" +} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml index 81467d6a62..a2cb3e6bde 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml @@ -13,14 +13,7 @@ TextWrapping="Wrap" /> - - - - - - - + SelectionChanged="StyleChooser_SelectionChanged" /> diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs index 84d08c01b2..eceb24e46a 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/AddVectorTiledLayerFromCustomStyle.xaml.cs @@ -8,7 +8,6 @@ // language governing permissions and limitations under the License. using ArcGIS.Samples.Managers; -using Esri.ArcGISRuntime.Geometry; using Esri.ArcGISRuntime.Mapping; using Esri.ArcGISRuntime.Portal; using Esri.ArcGISRuntime.Tasks.Offline; @@ -16,7 +15,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace ArcGIS.WinUI.Samples.AddVectorTiledLayerFromCustomStyle @@ -31,16 +29,14 @@ namespace ArcGIS.WinUI.Samples.AddVectorTiledLayerFromCustomStyle public partial class AddVectorTiledLayerFromCustomStyle { // ArcGIS Online portal item strings. - private readonly string[] _onlineItemIDs = + private readonly string[] _portalItemIDs = { "1349bfa0ed08485d8a92c442a3850b06", "bd8ac41667014d98b933e97713ba8377", "02f85ec376084c508b9c8e5a311724fa", "1bf0cc4a4380468fbbff107e100f65a5", - }; - private readonly string[] _offlineItemIDs = - { + // Offline custom style vector tiled layer will be created once a VTPK is exported. "e01262ef2a4f4d91897d9bbd3a9b1075", "ce8a34e5d4ca4fa193a097511daa8855" }; @@ -70,21 +66,27 @@ private async Task Initialize() ArcGISPortal portal = await ArcGISPortal.CreateAsync(); // Store a list of all portal items. - foreach (string item in _onlineItemIDs) - { - PortalItem portalItem = await PortalItem.CreateAsync(portal, item); - _vectorTiledLayers.Add(portalItem); - } - foreach (string item in _offlineItemIDs) + foreach (string itemID in _portalItemIDs) { - PortalItem portalItem = await PortalItem.CreateAsync(portal, item); + PortalItem portalItem = await PortalItem.CreateAsync(portal, itemID); _vectorTiledLayers.Add(portalItem); } // Create a map using defaults. MyMapView.Map = new Map() { InitialViewpoint = _defaultViewpoint }; - // By default, the UI label will not reflect the default style. + // Populate the combo box. + StyleChooser.ItemsSource = new string[] + { + "Default", + "Style 1", + "Style 2", + "Style 3", + "Offline custom style - Light", + "Offline custom style - Dark" + }; + + // Select the default style. StyleChooser.SelectedIndex = 0; // Export offline custom styles. @@ -98,31 +100,12 @@ private async Task Initialize() } } - private void StyleChooserSelectionChanged(object sender, SelectionChangedEventArgs e) - { - _ = ChangeStyle(sender as ComboBox); - } - - private async Task ChangeStyle(ComboBox styleChooser) + private async void StyleChooser_SelectionChanged(object sender, SelectionChangedEventArgs e) { try { - int styleIndex = styleChooser.SelectedIndex; - - // Check if the user selected an online or offline custom style. - // Create a new basemap with the appropriate style. - if (_onlineItemIDs.Contains(_vectorTiledLayers[styleIndex].ItemId)) - { - MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(_vectorTiledLayers[styleIndex])); - await MyMapView.SetViewpointAsync(_defaultViewpoint); - } - else - { - // Determine which cache to use based on if the style selected is light (index 4) or dark. - ItemResourceCache cache = styleIndex == 4 ? _lightStyleResourceCache : _darkStyleResourceCache; - MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(new VectorTileCache(_localVectorPackagePath), cache)); - await MyMapView.SetViewpointAsync(_dodgeCityViewpoint); - } + // Get the style name and index of the selected item. + await ChangeStyleAsync(StyleChooser.SelectedIndex, StyleChooser.SelectedItem.ToString()); } catch (Exception ex) { @@ -131,6 +114,26 @@ private async Task ChangeStyle(ComboBox styleChooser) } } + private async Task ChangeStyleAsync(int styleIndex, string styleName) + { + // Check if the user selected an online or offline custom style. + // Create a new basemap with the appropriate style. + if (styleName.Contains("Offline")) + { + // Determine which cache to use based on if the style selected is light or dark. + ItemResourceCache cache = styleName.Contains("Light") ? _lightStyleResourceCache : _darkStyleResourceCache; + + MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(new VectorTileCache(_localVectorPackagePath), cache)); + await MyMapView.SetViewpointAsync(_dodgeCityViewpoint); + await cache.LoadAsync(); + } + else + { + MyMapView.Map.Basemap = new Basemap(new ArcGISVectorTiledLayer(_vectorTiledLayers[styleIndex])); + await MyMapView.SetViewpointAsync(_defaultViewpoint); + } + } + private async Task ExportStyle(PortalItem vectorTiledLayer) { try @@ -139,13 +142,16 @@ private async Task ExportStyle(PortalItem vectorTiledLayer) ExportVectorTilesTask exportTask = await ExportVectorTilesTask.CreateAsync(vectorTiledLayer.Url); // Get the item resource path for the basemap styling. - string itemResourcePath = Path.Combine(Path.GetTempPath(), vectorTiledLayer.ItemId + "_styleItemResources"); + string itemResourceCachePath = Path.Combine(Path.GetTempPath(), vectorTiledLayer.ItemId + "_styleItemResources"); // If cache has been created previously, return. - if (Directory.Exists(itemResourcePath)) { return new ItemResourceCache(itemResourcePath); } + if (Directory.Exists(itemResourceCachePath) && (Directory.GetFiles(itemResourceCachePath).Length != 0)) + { + return new ItemResourceCache(itemResourceCachePath); + } // Create the export job and start it. - ExportVectorTilesJob job = exportTask.ExportStyleResourceCache(itemResourcePath); + ExportVectorTilesJob job = exportTask.ExportStyleResourceCache(itemResourceCachePath); job.Start(); // Wait for the job to complete. diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md index 7dd5e2a6e2..813c267976 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/AddVectorTiledLayerFromCustomStyle/readme.md @@ -16,16 +16,15 @@ Pan and zoom to explore the vector tile basemap. 1. Create a `PortalItem` for each vector tiled layer. 2. Create a `Map` and set the default `Viewpoint`. -3. Update the `Basemap` and `Viewpoint` when a new style is selected. -4. Export the light and dark offline custom styles. - i. Create a `ExportVectorTilesTask` using the portal item. - ii. Get the path for where the cache is being stored locally. - iii. Return with the cache if the path already exists. - iv. Else, create a `ExportVectorTilesJob` by having the task call `ExportStyleResourceCache` with the path as a parameter. - v. Start the job. - vi. When the job completes, store the result as a `ExportVectorTilesResult`. - vii. Return the result's item resource cache. -5. Update the basemap upon a style selection change. +3. Export the light and dark offline custom styles. + i. Create a `ExportVectorTilesTask` using the portal item. + ii. Get the path for where the cache is being stored locally. + iii. Return with the cache if the path already exists. + iv. Else, create a `ExportVectorTilesJob` by having the task call `ExportStyleResourceCache` with the path as a parameter. + v. Start the job. + vi. When the job completes, store the result as a `ExportVectorTilesResult`. + vii. Return the result's item resource cache. +4. Update the `Basemap` and `Viewpoint` when a new style is selected. ## Relevant API diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs index 5d2cbe98e5..7eb9f60fc9 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/CreateAndSaveKmlFile.xaml.cs @@ -11,11 +11,11 @@ using Esri.ArcGISRuntime.Geometry; using Esri.ArcGISRuntime.Mapping; using Esri.ArcGISRuntime.Ogc; -using Esri.ArcGISRuntime.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; @@ -29,7 +29,7 @@ namespace ArcGIS.WinUI.Samples.CreateAndSaveKmlFile category: "Layers", description: "Construct a KML document and save it as a KMZ file.", instructions: "Click on one of the buttons in the middle row to start adding a geometry. Click on the map view to place vertices. Click the \"Complete Sketch\" button to add the geometry to the KML document as a new KML placemark. Use the style interface to edit the style of the placemark. If you do not wish to set a style, click the \"Don't Apply Style\" button. When you are finished adding KML nodes, click on the \"Save KMZ file\" button to save the active KML document as a .kmz file on your system. Use the \"Reset\" button to clear the current KML document and start a new one.", - tags: new[] { "KML", "KMZ", "Keyhole", "OGC" })] + tags: new[] { "KML", "KMZ", "Keyhole", "OGC", "geometry editor" })] [ArcGIS.Samples.Shared.Attributes.OfflineData()] public partial class CreateAndSaveKmlFile { @@ -37,6 +37,7 @@ public partial class CreateAndSaveKmlFile private KmlDataset _kmlDataset; private KmlLayer _kmlLayer; private KmlPlacemark _currentPlacemark; + private GeometryType _geometryType; public CreateAndSaveKmlFile() { @@ -97,26 +98,23 @@ private async void Edit_Click(object sender, RoutedEventArgs e) CompleteButton.Visibility = Visibility.Visible; SaveResetGrid.Visibility = Visibility.Collapsed; - // Create variables for the sketch creation mode and color. - SketchCreationMode creationMode; - // Set the creation mode and UI based on which button called this method. switch (((Button)sender).Name) { case nameof(PointButton): - creationMode = SketchCreationMode.Point; + _geometryType = GeometryType.Point; InstructionsText.Text = "Tap to add a point."; StyleText.Text = "Select an icon for the placemark."; break; case nameof(PolylineButton): - creationMode = SketchCreationMode.Polyline; + _geometryType = GeometryType.Polyline; InstructionsText.Text = "Tap to add a vertex."; StyleText.Text = "Select a color for the placemark."; break; case nameof(PolygonButton): - creationMode = SketchCreationMode.Polygon; + _geometryType = GeometryType.Polygon; InstructionsText.Text = "Tap to add a vertex."; StyleText.Text = "Select a color for the placemark."; break; @@ -125,43 +123,14 @@ private async void Edit_Click(object sender, RoutedEventArgs e) return; } - // Get the user-drawn geometry. - Geometry geometry = await MyMapView.SketchEditor.StartAsync(creationMode, true); - - // Project the geometry to WGS84 (WGS84 is required by the KML standard). - Geometry projectedGeometry = geometry.Project(SpatialReferences.Wgs84); - - // Create a KmlGeometry using the new geometry. - KmlGeometry kmlGeometry = new KmlGeometry(projectedGeometry, KmlAltitudeMode.ClampToGround); - - // Create a new placemark. - _currentPlacemark = new KmlPlacemark(kmlGeometry); - - // Add the placemark to the KmlDocument. - _kmlDocument.ChildNodes.Add(_currentPlacemark); + // Start the geometry editor. + MyMapView.GeometryEditor.Start(_geometryType); - // Enable the style editing UI. - StyleBorder.Visibility = Visibility.Visible; - MainUI.Visibility = Visibility.Collapsed; - - // Display the Icon picker or the color picker based on the creation mode. - IconPicker.Visibility = creationMode == SketchCreationMode.Point ? Visibility.Visible : Visibility.Collapsed; - ColorSelector.Visibility = creationMode != SketchCreationMode.Point ? Visibility.Visible : Visibility.Collapsed; } catch (ArgumentException) { await new MessageDialog2("Unsupported Geometry", "Error").ShowAsync(); } - finally - { - // Reset the UI. - ShapesPanel.Visibility = Visibility.Visible; - CompleteButton.Visibility = Visibility.Collapsed; - InstructionsText.Text = "Select the type of feature you would like to add."; - - // Enable the save and reset buttons. - SaveResetGrid.Visibility = Visibility; - } } private void Apply_Style_Click(object sender, RoutedEventArgs e) @@ -210,11 +179,59 @@ private void Complete_Click(object sender, RoutedEventArgs e) { try { - // Finish the sketch. - MyMapView.SketchEditor.CompleteCommand.Execute(null); + // Get the user-drawn geometry. + Geometry geometry = MyMapView.GeometryEditor.Stop(); + + // Hold a reference for the new placemark geometry. + KmlGeometry kmlGeometry; + + // Check to see if a geometry has been drawn. + if (!geometry.IsEmpty) + { + + if (MyMapView.SpatialReference != null && + geometry.SpatialReference != MyMapView.SpatialReference) + { + // Project the geometry to WGS84 (WGS84 is required by the KML standard). + Geometry projectedGeometry = geometry.Project(SpatialReferences.Wgs84); + + // Create a KmlGeometry using the projected geometry. + kmlGeometry = new KmlGeometry(projectedGeometry, KmlAltitudeMode.ClampToGround); + } + else + { + // Create a KmlGeometry using the user-drawn geometry. + kmlGeometry = new KmlGeometry(geometry, KmlAltitudeMode.ClampToGround); + } + + // Create a new placemark. + _currentPlacemark = new KmlPlacemark(kmlGeometry); + + // Add the placemark to the KmlDocument. + _kmlDocument.ChildNodes.Add(_currentPlacemark); + + // Enable the style editing UI. + StyleBorder.Visibility = Visibility.Visible; + MainUI.Visibility = Visibility.Collapsed; + + // Display the Icon picker or the color picker based on the creation mode. + IconPicker.Visibility = _geometryType == GeometryType.Point ? Visibility.Visible : Visibility.Collapsed; + ColorSelector.Visibility = _geometryType != GeometryType.Point ? Visibility.Visible : Visibility.Collapsed; + } } - catch (ArgumentException) + catch (Exception ex) { + Debug.WriteLine(ex.Message); + } + finally + { + // Reset the UI. + ShapesPanel.Visibility = Visibility.Visible; + CompleteButton.Visibility = Visibility.Collapsed; + InstructionsText.Text = "Select the type of feature you would like to add."; + + // Enable the save and reset buttons. + SaveResetGrid.Visibility = Visibility; } } diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md index 11da843fc8..815c9a6b7d 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.md @@ -17,7 +17,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click 1. Create a `KmlDocument` 2. Create a `KmlDataset` using the `KmlDocument`. 3. Create a `KmlLayer` using the `KmlDataset` and add it to `Map.OperationalLayers`. -4. Create `Geometry` using `SketchEditor`. +4. Create `Geometry` using `GeometryEditor`. 5. Project that `Geometry` to WGS84 using `GeometryEngine.Project`. 6. Create a `KmlGeometry` object using that projected `Geometry`. 7. Create a `KmlPlacemark` using the `KmlGeometry`. @@ -27,6 +27,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click ## Relevant API +* GeometryEditor * GeometryEngine.Project * KmlDataset * KmlDocument @@ -35,8 +36,7 @@ Click on one of the buttons in the middle row to start adding a geometry. Click * KmlNode.SaveAsASync * KmlPlacemark * KmlStyle -* SketchEditor ## Tags -Keyhole, KML, KMZ, OGC \ No newline at end of file +geometry editor, Keyhole, KML, KMZ, OGC diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json index 9e63933e8e..1667df2c43 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/CreateAndSaveKmlFile/readme.metadata.json @@ -10,13 +10,15 @@ "KML", "KMZ", "Keyhole", - "OGC" + "OGC", + "geometry editor" ], "offline_data": [], "redirect_from": [ "/net/latest/winui/sample-code/createandsavekmlfile.htm" ], "relevant_apis": [ + "GeometryEditor", "GeometryEngine.Project", "KmlDataset", "KmlDocument", @@ -24,8 +26,7 @@ "KmlLayer", "KmlNode.SaveAsASync", "KmlPlacemark", - "KmlStyle", - "SketchEditor" + "KmlStyle" ], "snippets": [ "CreateAndSaveKmlFile.xaml.cs", diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg new file mode 100644 index 0000000000..4bf5b14fdf Binary files /dev/null and b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.jpg differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml new file mode 100644 index 0000000000..c6aa392c58 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs new file mode 100644 index 0000000000..514835a537 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/DisplayPointsUsingClusteringFeatureReduction.xaml.cs @@ -0,0 +1,81 @@ +// Copyright 2023 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +// language governing permissions and limitations under the License. + +using Esri.ArcGISRuntime.Data; +using Esri.ArcGISRuntime.Mapping; +using Esri.ArcGISRuntime.Mapping.Popups; +using Esri.ArcGISRuntime.Portal; +using Esri.ArcGISRuntime.UI.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Linq; +using System.Threading.Tasks; + +namespace ArcGIS.WinUI.Samples.DisplayPointsUsingClusteringFeatureReduction +{ + [ArcGIS.Samples.Shared.Attributes.Sample( + name: "Display points using clustering feature reduction", + category: "Layers", + description: "Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters.", + instructions: "Pan and zoom the map to view how clustering is dynamically updated. Toggle clustering off to view the original point features that make up the clustered elements. When clustering is on, you can click on a clustered geoelement to view aggregated information and summary statistics for that cluster. When clustering is toggled off and you click on the original feature you get access to information about individual power plant features.", + tags: new[] { "aggregate", "bin", "cluster", "group", "merge", "normalize", "reduce", "summarize" })] + [ArcGIS.Samples.Shared.Attributes.OfflineData()] + public partial class DisplayPointsUsingClusteringFeatureReduction + { + private FeatureLayer _layer; + + public DisplayPointsUsingClusteringFeatureReduction() + { + InitializeComponent(); + _ = Initialize(); + } + + private async Task Initialize() + { + // Get the power plants web map from the default portal. + var portal = await ArcGISPortal.CreateAsync(); + PortalItem portalItem = await PortalItem.CreateAsync(portal, "8916d50c44c746c1aafae001552bad23"); + + // Create a new map from the web map. + MyMapView.Map = new Map(portalItem); + + // Get the power plant feature layer once the map has finished loading. + await MyMapView.Map.LoadAsync(); + _layer = (FeatureLayer)MyMapView.Map.OperationalLayers.First(); + + PopupBackground.Tapped += (sender, args) => + { + PopupBackground.Visibility = Visibility.Collapsed; + PopupViewer.PopupManager = null; + }; + } + + private async void MyMapView_GeoViewTapped(object sender, GeoViewInputEventArgs e) + { + // Identify the tapped observation. + IdentifyLayerResult results = await MyMapView.IdentifyLayerAsync(_layer, e.Position, 3, true); + + // Return if no popups are found. + if (results.Popups.Count == 0) return; + + // Set the popup and make it visible. + PopupViewer.PopupManager = new PopupManager(results.Popups.FirstOrDefault()); + PopupBackground.Visibility = Visibility.Visible; + } + + // Enable clustering feature reduction if the checkbox has been checked, disable otherwise. + private void CheckBox_CheckChanged(object sender, RoutedEventArgs e) + { + // This event is raised when sample is initially loaded when layer is null. + if (_layer == null) return; + + _layer.FeatureReduction.IsEnabled = (bool)(sender as CheckBox).IsChecked; + } + } +} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md new file mode 100644 index 0000000000..78175dc49e --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.md @@ -0,0 +1,38 @@ +# Display points using clustering feature reduction + +Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters. + +![](DisplayPointsUsingClusteringFeatureReduction.jpg) + +## Use case + +Feature clustering can be used to dynamically aggregate groups of points that are within proximity of each other in order to represent each group with a single symbol. Such grouping allows you to see patterns in the data that are difficult to visualize when a layer contains hundreds or thousands of points that overlap and cover each other. + +## How to use the sample + +Pan and zoom the map to view how clustering is dynamically updated. Toggle clustering off to view the original point features that make up the clustered elements. When clustering is on, you can click on a clustered geoelement to view aggregated information and summary statistics for that cluster. When clustering is toggled off and you click on the original feature you get access to information about individual power plant features. + +## How it works + +1. Create a map from a web map `PortalItem`. +2. Get the cluster enabled layer from the map's operational layers. +3. Get the `FeatureReduction` from the feature layer and set the `IsEnabled` bool to enable or disable clustering on the feature layer. +4. When the user clicks on the map, call `IdentifyFeatureLayerAsync()`, passing in the layer, map click location, tolerance, and `returnPopupsOnly` as true. +5. Set the `Popup` from the resulting `IdentifyLayerResult` to the `PopupViewer.Popup`. +6. Make the `PopupViewer` visible. + +## Relevant API + +* AggregateGeoElement +* FeatureLayer +* FeatureReduction +* GeoElement +* IdentifyLayerResult + +## About the data + +This sample uses a [web map](https://www.arcgis.com/home/item.html?id=8916d50c44c746c1aafae001552bad23) that displays the Esri [Global Power Plants](https://www.arcgis.com/home/item.html?id=eb54b44c65b846cca12914b87b315169) feature layer with feature reduction enabled. When enabled, the aggregate features symbology shows the color of the most common power plant type, and a size relative to the average plant capacity of the cluster. + +## Tags + +aggregate, bin, cluster, group, merge, normalize, reduce, summarize diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json new file mode 100644 index 0000000000..47c3b09398 --- /dev/null +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/DisplayPointsUsingClusteringFeatureReduction/readme.metadata.json @@ -0,0 +1,35 @@ +{ + "category": "Layers", + "description": "Display a web map with a point feature layer that has feature reduction enabled to aggregate points into clusters.", + "formal_name": "DisplayPointsUsingClusteringFeatureReduction", + "ignore": false, + "images": [ + "DisplayPointsUsingClusteringFeatureReduction.jpg" + ], + "keywords": [ + "aggregate", + "bin", + "cluster", + "group", + "merge", + "normalize", + "reduce", + "summarize" + ], + "offline_data": [], + "redirect_from": [ + "/net/latest/winui/sample-code/displaypointsusingclusteringfeaturereduction.htm" + ], + "relevant_apis": [ + "AggregateGeoElement", + "FeatureLayer", + "FeatureReduction", + "GeoElement", + "IdentifyLayerResult" + ], + "snippets": [ + "DisplayPointsUsingClusteringFeatureReduction.xaml.cs", + "DisplayPointsUsingClusteringFeatureReduction.xaml" + ], + "title": "Display points using clustering feature reduction" +} \ No newline at end of file diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg index d038c4098e..e06f6e2d73 100644 Binary files a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg and b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.jpg differ diff --git a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml index 71a1310f53..b25c02c55e 100644 --- a/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml +++ b/src/WinUI/ArcGIS.WinUI.Viewer/Samples/Layers/PlayKmlTours/PlayKmlTours.xaml @@ -5,55 +5,34 @@ - - - - - - - - - - - - -