varsceneLayerUrl=@"https://myportal.com/server/rest/services/Hosted/SceneLayerServiceName/SceneServer";//portal items also ok as long as the portal is the current active portal...//var sceneLayerUrl = @"https://myportal.com/home/item.html?id=123456789abcdef1234567890abcdef0";awaitQueuedTask.Run(()=>{//Create with initial visibility set to false. Add to current scenevarcreateparams=newLayerCreationParams(newUri(sceneLayerUrl,UriKind.Absolute)){IsVisible=false};//cast to specific type of scene layer being created - in this case FeatureSceneLayervarsceneLayer=LayerFactory.Instance.CreateLayer<Layer>(createparams,MapView.Active.Map)asFeatureSceneLayer;//or...specify the cast directlyvarsceneLayer2=LayerFactory.Instance.CreateLayer<FeatureSceneLayer>(createparams,MapView.Active.Map);//ditto for BuildingSceneLayer, PointCloudSceneLayer, IntegratedMeshSceneLayer//...});
Enumerate the Discipline Layers from a Building SceneLayer
publicvoidQueryBuildingSceneLayer(){varbldgLayer=MapView.Active.Map.GetLayersAsFlattenedList().OfType<BuildingSceneLayer>().First();vardisciplines=newDictionary<string,BuildingDisciplineSceneLayer>();//A Building layer has two children - Overview and FullModel//Overview is a FeatureSceneLayer//Full Model is a BuildingDisciplineSceneLayer that contains the disciplinesvarfullModel=bldgLayer.FindLayers("Full Model").First()asBuildingDisciplineSceneLayer;CollectDisciplineLayers(fullModel,disciplines);}internalvoidCollectDisciplineLayers(BuildingDisciplineSceneLayerdisciplineLayer,Dictionary<string,BuildingDisciplineSceneLayer>disciplines){//collect information on the disciplinesvarname=disciplineLayer.Name;varlayerType=((ISceneLayerInfo)disciplineLayer).SceneServiceLayerType.ToString();vardiscipline=disciplineLayer.GetDiscipline();//etc//TODO - use collected informationdisciplines.Add(discipline,disciplineLayer);//Discipline layers are composite layers tooforeach(varchildDisciplineindisciplineLayer.Layers.OfType<BuildingDisciplineSceneLayer>()){//Discipline layers can also contain FeatureSceneLayers that render the//individual full model contentsvarcontent_names=string.Join(", ",childDiscipline.Layers.OfType<FeatureSceneLayer>().Select(fl =>fl.Name));CollectDisciplineLayers(childDiscipline,disciplines);}}
Query Building Scene Layer for available Types and Values
//Must be called on the MCT//Retrieve the complete set of types and values for the building scene//var bsl = ...;vardict=bsl.QueryAvailableFieldsAndValues();//get a list of existing disciplinesvardisciplines=dict.SingleOrDefault(kvp =>kvp.Key=="Discipline").Value??newList<string>();//get a list of existing categoriesvarcategories=dict.SingleOrDefault(kvp =>kvp.Key=="Category").Value??newList<string>();//get a list of existing floors or "levels"varfloors=dict.SingleOrDefault(kvp =>kvp.Key=="BldgLevel").Value??newList<string>();
Create a Default Filter and Get Filter Count
//Must be called on the MCT//Creates a default filter on the building scene//var bsl = ...;varfilter1=bsl.CreateDefaultFilter();varvalues=filter1.FilterBlockDefinitions[0].SelectedValues;//values will be a single value for the type//"CreatedPhase", value "New Construction"//There will be at least one filter after "CreateDefaultFilter()" callvarfiltersCount=bsl.GetFilters().Count;
Get all the Filters that Contain WireFrame Blocks
//var bsl = ...;//Note: wire_frame_filters can be null in this examplevarwire_frame_filters=bsl.GetFilters().Where(
f =>f.FilterBlockDefinitions.Any(fb =>fb.FilterBlockMode==Object3DRenderingMode.Wireframe));//substitute Object3DRenderingMode.None to get blocks with a solid mode (default)//and...//fb.FilterBlockMode == Object3DRenderingMode.Wireframe &&//fb.FilterBlockMode == Object3DRenderingMode.None//for blocks with both
Set and Clear Active Filter for BuildingSceneLayer
//Must be called on the MCT//Note: Use HasFilter to check if a given filter id exists in the layer//var bsl = ...;if(bsl.HasFilter(filter1.ID))bsl.SetActiveFilter(filter1.ID);varactiveFilter=bsl.GetActiveFilter();//Clear the active filterbsl.ClearActiveFilter();
Get BuildingSceneLayer Filter ID and Filter
stringfilterID1=filter1.ID;varfilter=bsl.GetFilter(filterID1);//or via Linq//var filter = bsl.GetFilters().FirstOrDefault(f => f.ID == filterID1);
Modify BuildingSceneLayer Filter Name and Description
//Must be called on the MCT//var bsl = ...;//var filter1 = bsl.GetFilter(filterID1);filter1.Name="Updated Filter Name";filter1.Description="Updated Filter description";bsl.SetFilter(filter1);
Create a Filter using Building Level and Category
//refer to "Query Building Scene Layer for available Types and Values"//...//var bsl = ...;//var dict = bsl.QueryAvailableFieldsAndValues();//var categories = dict.SingleOrDefault(kvp => kvp.Key == "Category").Value;//var floors = dict.SingleOrDefault(kvp => kvp.Key == "BldgLevel").Value;//Make a new filter definitionvarfd=newFilterDefinition(){Name="Floor and Category Filter",Description="Example filter",};//Set up the values for the filtervarfiltervals=newDictionary<string,List<string>>();filtervals.Add("BldgLevel",newList<string>(){floors[0]});varcategory_vals=categories.Where(v =>v=="Walls"||v=="Stairs").ToList()??newList<string>();if(category_vals.Count()>0){filtervals.Add("Category",category_vals);}//Create a solid block (other option is "Wireframe")varfdef=newFilterBlockDefinition(){FilterBlockMode=Object3DRenderingMode.None,Title="Solid Filter",SelectedValues=filtervals//Floor and Category};//Apply the blockfd.FilterBlockDefinitions=newList<FilterBlockDefinition>(){fdef};//Add the filter definition to the layerbsl.SetFilter(fd);//Set it active. The ID is auto-generatedbsl.SetActiveFilter(fd.ID);
Modify BuildingSceneLayer Filter Block
//Must be called on the MCT//Assuming retrieve filter ok//var bsl = ...;//var filter1 = bsl.GetFilter(...);varfilterBlock=newFilterBlockDefinition();filterBlock.FilterBlockMode=Object3DRenderingMode.Wireframe;varselectedValues=newDictionary<string,List<string>>();//We assume QueryAvailableFieldsAndValues() contains "Walls" and "Doors"//For 'Category'selectedValues["Category"]=newList<string>(){"Walls","Doors"};filterBlock.SelectedValues=selectedValues;//Overwritefilter1.FilterBlockDefinitions=newList<FilterBlockDefinition>(){filterBlock};bsl.SetFilter(filter1);
Remove BuildingSceneLayer Filter
//var bsl = ...;//Note: Use HasFilter to check if a given filter id exists in the layer//Must be called on the MCTif(bsl.HasFilter(filter1.ID))bsl.RemoveFilter(filter1.ID);//Or remove all filtersbsl.RemoveAllFilters();
//must support editing!//var featSceneLayer = ... ;if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable)return;//Check geometry type...must be point in this exampleif(featSceneLayer.ShapeType!=esriGeometryType.esriGeometryPoint)return;vareditOp=newEditOperation(){Name="Create new 3d point feature",SelectNewFeatures=true};varattributes=newDictionary<string,object>();//mapPoint contains the new 3d point locationattributes.Add("SHAPE",mapPoint);attributes.Add("TreeID","1");editOp.Create(featSceneLayer,attributes);editOp.ExecuteAsync();//fyi, no await
Delete all the selected features in FeatureSceneLayer
//must support editing!//var featSceneLayer = .... ;if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable)return;vardelOp=newEditOperation(){Name="Delete selected features"};//Assuming we have a selection on the layer...delOp.Delete(featSceneLayer,featSceneLayer.GetSelection().GetObjectIDs());awaitdelOp.ExecuteAsync();//await if needed but not required
Edit the attributes of a FeatureSceneLayer
//must support editing!varfeatSceneLayer=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureSceneLayer>().FirstOrDefault();if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable)return;varok=awaitQueuedTask.Run(()=>{vareditOp=newEditOperation(){Name="Edit FeatureSceneLayer Attributes",SelectModifiedFeatures=true};//make an inspectorvarinspector=newInspector();//get the attributes for the specified oidinspector.Load(featSceneLayer,oid);inspector["PermitNotes"]="test";//modifyeditOp.Modify(inspector);returneditOp.Execute();//synchronous flavor});
//Must be called on the MCTvardataSourceType=featSceneLayer?.GetDataSourceType()??SceneLayerDataSourceType.Unknown;if(dataSourceType==SceneLayerDataSourceType.SLPK){//Uses SLPK - only cached attributes}elseif(dataSourceType==SceneLayerDataSourceType.Service){//Hosted service - can have live attributes - check HasAssociatedFeatureService}
Get the Associated Feature class
//var featSceneLayer = ....;if(featSceneLayer.HasAssociatedFeatureService){//Must be called on the MCTusing(varfc=featSceneLayer.GetFeatureClass()){//TODO query underlying feature class}}
Get Field Definitions
//var featSceneLayer = ....;awaitQueuedTask.Run(()=>{//Get only the readonly fieldsvarreadOnlyFields=featSceneLayer.GetFieldDescriptions().Where(fdesc =>fdesc.IsReadOnly);//etc});
Set a Definition Query
//Must be called on the MCT//var featSceneLayer = ...;featSceneLayer.SetDefinitionQuery("Name = 'Ponderosa Pine'");
Select features via the MapView
//assume the geometry used in SelectFeaturesEx() is coming from a //map tool...////SketchType = SketchGeometryType.Rectangle;//SketchOutputMode = SketchOutputMode.Screen;awaitQueuedTask.Run(()=>{varresult=MapView.Active.SelectFeaturesEx(geometry);//Get scene layers with selectionsvarscene_layers=result.Where(kvp =>kvp.KeyisFeatureSceneLayer);foreach(varkvpinscene_layers){varscene_layer=kvp.KeyasFeatureSceneLayer;varsel_oids=kvp.Value;//If there are attributes then get themif(scene_layer.HasAssociatedFeatureService){varinsp=newInspector();foreach(varoidinsel_oids){insp.Load(scene_layer,oid);//TODO something with retrieved attributes}}}});
Has Associated FeatureService
varfeatSceneLayer=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureSceneLayer>().First();if(featSceneLayer.HasAssociatedFeatureService){//Can Select and Search...possibly edit}
Search Rows on the FeatureSceneLayer
//var featSceneLayer = ...;if(!featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supported//Multipatch (Object3D) or point?varis3dObject=((ISceneLayerInfo)featSceneLayer).SceneServiceLayerType==esriSceneServiceLayerType.Object3D;awaitQueuedTask.Run(()=>{varqueryFilter=newQueryFilter{WhereClause="Name = 'Ponderosa Pine'",SubFields="*"};introwCount=0;//or select... var select = featSceneLayer.Select(queryFilter)using(RowCursorrowCursor=featSceneLayer.Search(queryFilter)){while(rowCursor.MoveNext()){using(varfeature=rowCursor.CurrentasFeature){varoid=feature.GetObjectID();varshape=feature.GetShape();varattrib=feature["Name"];if(is3dObject){//shape is a multipatch}else{//shape is a point}rowCount+=1;}}}});
Hide Selected features and Show Hidden features
//var featSceneLayer = ...;if(featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supportedawaitQueuedTask.Run(()=>{QueryFilterqf=newQueryFilter(){ObjectIDs=newList<long>(){6069,6070,6071},SubFields="*"};featSceneLayer.Select(qf,SelectionCombinationMethod.New);featSceneLayer.HideSelectedFeatures();varselectionCount=featSceneLayer.SelectionCount;featSceneLayer.ShowHiddenFeatures();selectionCount=featSceneLayer.SelectionCount;});
Use MapView Selection SelectFeaturesEx or GetFeaturesEx
//var featSceneLayer = ...;varsname=featSceneLayer.Name;awaitQueuedTask.Run(()=>{//Select all features within the current map viewvarsz=MapView.Active.GetViewSize();varc_ll=newCoordinate2D(0,0);varc_ur=newCoordinate2D(sz.Width,sz.Height);//Use screen coordinates for 3D selection on MapViewvarenv=EnvelopeBuilder.CreateEnvelope(c_ll,c_ur);//HasAssociatedFeatureService does not matter for SelectFeaturesEx//or GetFeaturesExvarresult=MapView.Active.SelectFeaturesEx(env);//var result = MapView.Active.GetFeaturesEx(env);//The list of object ids from SelectFeaturesExvaroids1=result.Where(kvp =>kvp.Key.Name==sname).First().Value;//TODO - use the object idsMapView.Active.Map.ClearSelection();});
Use Select or Search with a Spatial Query
//var featSceneLayer = ...;//var sname = featSceneLayer.Name;awaitQueuedTask.Run(()=>{if(!featSceneLayer.HasAssociatedFeatureService)return;//no search or select//Select all features within the current map viewvarsz=MapView.Active.GetViewSize();varmap_pt1=MapView.Active.ClientToMap(newSystem.Windows.Point(0,sz.Height));varmap_pt2=MapView.Active.ClientToMap(newSystem.Windows.Point(sz.Width,0));//Convert to an envelopevartemp_env=EnvelopeBuilder.CreateEnvelope(map_pt1,map_pt2,MapView.Active.Map.SpatialReference);//Project if needed to the layer spatial refSpatialReferencesr=null;using(varfc=featSceneLayer.GetFeatureClass())using(varfdef=fc.GetDefinition())sr=fdef.GetSpatialReference();varenv=GeometryEngine.Instance.Project(temp_env,sr)asEnvelope;//Set up a query filtervarsf=newSpatialQueryFilter(){FilterGeometry=env,SpatialRelationship=SpatialRelationship.Intersects,SubFields="*"};//Select against the feature servicevarselect=featSceneLayer.Select(sf);if(select.GetCount()>0){//enumerate over the selected featuresusing(varrc=select.Search()){while(rc.MoveNext()){using(varfeature=rc.CurrentasFeature){varoid=feature.GetObjectID();//etc.}}}}MapView.Active.Map.ClearSelection();});
Query all class codes and lables in the PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;Dictionary<int,string>classCodesAndLabels=pcsl.QueryAvailableClassCodesAndLabels();
Set a Filter for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;//Retrieve the available classification codesvardict=pcsl.QueryAvailableClassCodesAndLabels();//Filter out low noise and unclassified (7 and 1 respectively)//consult https://pro.arcgis.com/en/pro-app/help/data/las-dataset/storing-lidar-data.htmvarfilterDef=newPointCloudFilterDefinition(){ClassCodes=dict.Keys.Where(c =>c!=7&&c!=1).ToList(),ReturnValues=newList<PointCloudReturnType>{PointCloudReturnType.FirstOfMany}};//apply the filterpcsl.SetFilters(filterDef.ToCIM());
Update the ClassFlags for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;varfilters=pcsl.GetFilters();PointCloudFilterDefinitionfdef=null;if(filters.Count()==0){fdef=newPointCloudFilterDefinition(){//7 is "edge of flight line" - excludeClassFlags=newList<ClassFlag>{newClassFlag(7,ClassFlagOption.Exclude)}};}else{fdef=PointCloudFilterDefinition.FromCIM(filters);//keep any include or ignore class flagsvarkeep=fdef.ClassFlags.Where(cf =>cf.ClassFlagOption!=ClassFlagOption.Exclude).ToList();//7 is "edge of flight line" - excludekeep.Add(newClassFlag(7,ClassFlagOption.Exclude));fdef.ClassFlags=keep;}//applypcsl.SetFilters(fdef.ToCIM());
Get the filters for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;IReadOnlyList<CIMPointCloudFilter>updatedFilter=pcsl.GetFilters();foreach(varfilterinupdatedFilter){//There is either 0 or 1 of eachif(filterisCIMPointCloudReturnFilterreturnFilter){PointCloudFilterDefinitionpcfl=PointCloudFilterDefinition.FromCIM(updatedFilter);List<PointCloudReturnType>updatedReturnValues=pcfl.ReturnValues;}if(filterisCIMPointCloudValueFilterclassCodesFilter){// do something}if(filterisCIMPointCloudBitFieldFilterclassFlagsFilter){// do something}}
Clear filters in PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;pcsl.ClearFilters();
Get PointCloudSceneLayer Renderer and RendererType
//Must be called on the MCT//var pcsl = ...;CIMPointCloudRenderercimGetPCLRenderer=pcsl.GetRenderer();//Can be one of Unknown, Stretch, ClassBreaks, UniqueValue, RGBPointCloudRendererTypepclRendererType=pcsl.RendererType;
Query PointCloudSceneLayer Renderer fields
//Must be called on the MCT//var pcsl = ...;IReadOnlyList<string>flds=pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.UniqueValueRenderer);varfldCount=flds.Count;
Create and Set a Stretch Renderer
//Must be called on the MCT//var pcsl = ...;varfields=pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.StretchRenderer);varstretchDef=newPointCloudRendererDefinition(PointCloudRendererType.StretchRenderer){//Will be either ELEVATION or INTENSITYField=fields[0]};//Create the CIM RenderervarstretchRenderer=pcsl.CreateRenderer(stretchDef)asCIMPointCloudStretchRenderer;//Apply a color rampvarstyle=Project.Current.GetItems<StyleProjectItem>().First(s =>s.Name=="ArcGIS Colors");varcolorRamp=style.SearchColorRamps("").First();stretchRenderer.ColorRamp=colorRamp.ColorRamp;//Apply modulationstretchRenderer.ColorModulation=newCIMColorModulationInfo(){MinValue=0,MaxValue=100};//apply the rendererpcsl.SetRenderer(stretchRenderer);
Create and Set a ClassBreaks Renderer
//Must be called on the MCT//var pcsl = ...;varfields=pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.ClassBreaksRenderer);varclassBreakDef=newPointCloudRendererDefinition(PointCloudRendererType.ClassBreaksRenderer){//ELEVATION or INTENSITYField=fields[0]};//create the renderervarcbr=pcsl.CreateRenderer(classBreakDef)asCIMPointCloudClassBreaksRenderer;//Set up a color scheme to usevarstyle=Project.Current.GetItems<StyleProjectItem>().First(s =>s.Name=="ArcGIS Colors");varrampStyle=style.LookupItem(StyleItemType.ColorRamp,"Spectrum By Wavelength-Full Bright_Multi-hue_2")asColorRampStyleItem;varcolorScheme=rampStyle.ColorRamp;//Set up 6 manual class breaksvarbreaks=6;varcolors=ColorFactory.Instance.GenerateColorsFromColorRamp(colorScheme,breaks);varclassBreaks=newList<CIMColorClassBreak>();varmin=cbr.Breaks[0].UpperBound;varmax=cbr.Breaks[cbr.Breaks.Count()-1].UpperBound;varstep=(max-min)/(double)breaks;//add in the class breaksdoubleupper=min;for(intb=1;b<=breaks;b++){doublelower=upper;upper=b==breaks?max:min+(b*step);varcb=newCIMColorClassBreak(){UpperBound=upper,Label=string.Format("{0:#0.0#} - {1:#0.0#}",lower,upper),Color=colors[b-1]};classBreaks.Add(cb);}cbr.Breaks=classBreaks.ToArray();pcsl.SetRenderer(cbr);
PointCloudSceneLayer Extended Properties
Edit Color Modulation
//Must be called on the MCT//var pcsl = ...;vardef=pcsl.GetDefinition()asCIMPointCloudLayer;//Get the ColorModulation off the renderervarmodulation=def.Renderer.ColorModulation;if(modulation==null)modulation=newCIMColorModulationInfo();//Set the minimum and maximum intensity as neededmodulation.MinValue=0;modulation.MaxValue=100.0;//apply backdef.Renderer.ColorModulation=modulation;//Commit changes back to the CIMpcsl.SetDefinition(def);
Edit The Renderer to use Fixed Size
//Must be called on the MCT//var pcsl = ...;vardef=pcsl.GetDefinition()asCIMPointCloudLayer;//Set the point shape and sizing on the rendererdef.Renderer.PointShape=PointCloudShapeType.DiskShaded;varpointSize=newCIMPointCloudFixedSizeAlgorithm(){UseRealWorldSymbolSizes=false,Size=8};def.Renderer.PointSizeAlgorithm=pointSize;//Commit changes back to the CIMpcsl.SetDefinition(def);
Edit the Renderer to Scale Size
//Must be called on the MCT//var pcsl = ...;//var def = pcsl.GetDefinition() as CIMPointCloudLayer;//Set the point shape and sizing on the rendererdef.Renderer.PointShape=PointCloudShapeType.DiskFlat;//defaultvarscaleSize=newCIMPointCloudSplatAlgorithm(){MinSize=8,ScaleFactor=1.0//100%};def.Renderer.PointSizeAlgorithm=scaleSize;//Commit changes back to the CIMpcsl.SetDefinition(def);
Edit Density settings
//Must be called on the MCT//var pcsl = ...;vardef=pcsl.GetDefinition()asCIMPointCloudLayer;//PointsBudget - corresponds to Display Limit on the UI// - the absolute maximum # of points to displaydef=pcsl.GetDefinition()asCIMPointCloudLayer;def.PointsBudget=1000000;//PointsPerInch - corresponds to Density Min --- Max on the UI// - the max number of points per display inch to rendererdef.PointsPerInch=15;//Commit changes back to the CIMpcsl.SetDefinition(def);