Accessing CodedValueDomain from FeatureType

3893
1
Jump to solution
03-24-2015 03:20 PM
RiverTaig1
Occasional Contributor

The coded value domains that I need to access are stored at the subtype level rather than the class level.  I'm unclear on how to gain access to these through the API.  I've successfully used AsyncTask, and in the PostExecute method, I get a FeatureResult back (the results of my query). Of course, the values I have for certain fields are coded (e.g. "W" means "Wood" and "S" means "Steel").  Unfortunately, I don't see how in the documentation to get at the subtypes (which I think are known as a FeatureType class), in order to get to the CodedValueDomains.  A more general question is how can I effectively use the documentation to discover the answer myself!

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
RiverTaig1
Occasional Contributor

I can answer my own question here after trying various things.  As a recap, I was trying to access coded value domains (both at the class level and at the subtype level).  My first mistake was attempting to get the LayerInfos for the ArcGISDynamicMapServiceLayer before it was available.  I was trying, unsuccessfully, to get these layers in the Activity's onCreate method.  What was ultimately successful was to set a listener in onCreate for the mapview's OnStatusChangedListener, and if the status was INITIALIZED, then fetch the domain values.

The code is ripe for critique and optimization I'm sure.  Certainly there is a substantial cost to fetching the LayerServiceInfos for each sublayer of each DynamicMapService layer.  But it works, and in my case, the performance is quite reasonable.

@Override

protected void onCreate(Bundle savedInstanceState) {

//...code (layerInfo not available yet!!

  mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {

      @Override

      public void onStatusChanged(Object source, STATUS status) {

        if (source == mMapView && status == STATUS.INITIALIZED) {

          FetchDomainValues();

        }

}

In FetchDomainValue, I loop through each ArcGISDynamicMapServiceLayer and then through each sublayer of that layer. A call to the method (FetchLayerSerivceInfoAsync) is used there to gain access to the domains.

public void FetchDomainValues(){

    Layer[] allLayers = mMapView.getLayers();

    for(Layer lyr : allLayers){

        if(lyr instanceof ArcGISDynamicMapServiceLayer){

            ArcGISDynamicMapServiceLayer agsdmsl = (ArcGISDynamicMapServiceLayer) lyr;

            ArcGISLayerInfo[] subLayers = agsdmsl.getAllLayers();

            for(ArcGISLayerInfo li : subLayers)

            {

                int subLayerIndex = (int) li.getId();

                FetchLayerServiceInfoAsync(agsdmsl, subLayerIndex);

            }

        }

    }

}

The actual coded value domains are retrieved asynchronously, and loaded into a String/String HashMap

private static HashMap<String,String> mCodedValues = new HashMap<String,String>();

//Load coded value domains into a String/String hash map where the

// keys are "layerID:subtype:fieldName:databaseValue"

// and the values are the "friendly" value to be displayed (e.g. "2:1:MATERIAL:W" > "Wood")

// would mean for layer 2, subtype 1, when the material field has a DB value of "W", the value

//to be displayed is "Wood".

//If the subtype = -1, that indicates the coded value domain is not at the subtype level,

//but rather at the class level.

public static void FetchLayerServiceInfoAsync(ArcGISDynamicMapServiceLayer agsdmsl, int layerIndex){

    Future resultFuture = agsdmsl.fetchLayerServiceInfo(layerIndex, new CallbackListener() {

    public void onError(Throwable e) {

        e.printStackTrace();

    }

    public void onCallback(Object x) {

        int layerIDInt = ((LayerServiceInfo)x).getId();

        String subTypeField = ((LayerServiceInfo)x).getTypeIdField();

        mLayerIndexToSubtypeFieldName.put(layerIDInt,subTypeField);

        String layerID = Integer.toString (layerIDInt);

        Field[] fields = ((LayerServiceInfo) x).getFields();

        //Load class level domains

        for(Field field : fields){

            Domain dom = field.getDomain();

            if( (dom != null) && (dom instanceof CodedValueDomain) ){

                AddCodedPairsIntoMap(dom,layerID,"-1",field.getName().toUpperCase());

            }

        }

        //Load Subtype level domains

        for (FeatureType ft : ((LayerServiceInfo) x).getSubTypes()) {

            String ftID = ft.getId(); //SubTypeID

            HashMap<String, Domain> domains = ft.getDomains();

            for (HashMap.Entry<String, Domain> domEntry : domains.entrySet()) {

                String domainName = domEntry.getKey();

                domainName = domainName.substring(1 + domainName.lastIndexOf("."));

                Domain domain = domEntry.getValue();

                if (domain instanceof CodedValueDomain) {

                    AddCodedPairsIntoMap(domain,layerID,ftID,domainName.toUpperCase());

                }

            }

        }

    }});

}

//the parameter ftID is the subtype identifier

private static void AddCodedPairsIntoMap(Domain domain,String layerID, String ftID, String domainName){

    Map<String, String> cvdPairs = ((CodedValueDomain) domain).getCodedValues();

    for (Map.Entry<String, String> entry : cvdPairs.entrySet()) {

        String dbValue = entry.getKey();

        String friendlyValue = entry.getValue();

        String key = layerID + ":" + ftID + ":" + domainName + ":" + dbValue;

        mCodedValues.put(key, friendlyValue);

    }

}

The last helper function, GetDomainValue is used to simply return the values that are stored in the coded value domains.

public static String GetDomainValue(String layerName,String subType, String fieldName, String dbValue){

    try {

        String requestedKey = layerName + ":" + subType + ":" + fieldName.toUpperCase() + ":" + dbValue;

        if(mCodedValues.containsKey(requestedKey)){

            Object retVal = mCodedValues.get(requestedKey);

            return  retVal.toString();

        }

        else{

            String defaultKey = layerName + ":-1:" + fieldName.toUpperCase() + ":" + dbValue;

            if(mCodedValues.containsKey(defaultKey)){

                Object retVal = mCodedValues.get(defaultKey);

                return  retVal.toString();

            }

            return  dbValue;

        }

    }

    catch (Exception ig){

        Log.e("BarCode|GetDomainValue",ig.getMessage());

        return dbValue;

    }

}

View solution in original post

0 Kudos
1 Reply
RiverTaig1
Occasional Contributor

I can answer my own question here after trying various things.  As a recap, I was trying to access coded value domains (both at the class level and at the subtype level).  My first mistake was attempting to get the LayerInfos for the ArcGISDynamicMapServiceLayer before it was available.  I was trying, unsuccessfully, to get these layers in the Activity's onCreate method.  What was ultimately successful was to set a listener in onCreate for the mapview's OnStatusChangedListener, and if the status was INITIALIZED, then fetch the domain values.

The code is ripe for critique and optimization I'm sure.  Certainly there is a substantial cost to fetching the LayerServiceInfos for each sublayer of each DynamicMapService layer.  But it works, and in my case, the performance is quite reasonable.

@Override

protected void onCreate(Bundle savedInstanceState) {

//...code (layerInfo not available yet!!

  mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {

      @Override

      public void onStatusChanged(Object source, STATUS status) {

        if (source == mMapView && status == STATUS.INITIALIZED) {

          FetchDomainValues();

        }

}

In FetchDomainValue, I loop through each ArcGISDynamicMapServiceLayer and then through each sublayer of that layer. A call to the method (FetchLayerSerivceInfoAsync) is used there to gain access to the domains.

public void FetchDomainValues(){

    Layer[] allLayers = mMapView.getLayers();

    for(Layer lyr : allLayers){

        if(lyr instanceof ArcGISDynamicMapServiceLayer){

            ArcGISDynamicMapServiceLayer agsdmsl = (ArcGISDynamicMapServiceLayer) lyr;

            ArcGISLayerInfo[] subLayers = agsdmsl.getAllLayers();

            for(ArcGISLayerInfo li : subLayers)

            {

                int subLayerIndex = (int) li.getId();

                FetchLayerServiceInfoAsync(agsdmsl, subLayerIndex);

            }

        }

    }

}

The actual coded value domains are retrieved asynchronously, and loaded into a String/String HashMap

private static HashMap<String,String> mCodedValues = new HashMap<String,String>();

//Load coded value domains into a String/String hash map where the

// keys are "layerID:subtype:fieldName:databaseValue"

// and the values are the "friendly" value to be displayed (e.g. "2:1:MATERIAL:W" > "Wood")

// would mean for layer 2, subtype 1, when the material field has a DB value of "W", the value

//to be displayed is "Wood".

//If the subtype = -1, that indicates the coded value domain is not at the subtype level,

//but rather at the class level.

public static void FetchLayerServiceInfoAsync(ArcGISDynamicMapServiceLayer agsdmsl, int layerIndex){

    Future resultFuture = agsdmsl.fetchLayerServiceInfo(layerIndex, new CallbackListener() {

    public void onError(Throwable e) {

        e.printStackTrace();

    }

    public void onCallback(Object x) {

        int layerIDInt = ((LayerServiceInfo)x).getId();

        String subTypeField = ((LayerServiceInfo)x).getTypeIdField();

        mLayerIndexToSubtypeFieldName.put(layerIDInt,subTypeField);

        String layerID = Integer.toString (layerIDInt);

        Field[] fields = ((LayerServiceInfo) x).getFields();

        //Load class level domains

        for(Field field : fields){

            Domain dom = field.getDomain();

            if( (dom != null) && (dom instanceof CodedValueDomain) ){

                AddCodedPairsIntoMap(dom,layerID,"-1",field.getName().toUpperCase());

            }

        }

        //Load Subtype level domains

        for (FeatureType ft : ((LayerServiceInfo) x).getSubTypes()) {

            String ftID = ft.getId(); //SubTypeID

            HashMap<String, Domain> domains = ft.getDomains();

            for (HashMap.Entry<String, Domain> domEntry : domains.entrySet()) {

                String domainName = domEntry.getKey();

                domainName = domainName.substring(1 + domainName.lastIndexOf("."));

                Domain domain = domEntry.getValue();

                if (domain instanceof CodedValueDomain) {

                    AddCodedPairsIntoMap(domain,layerID,ftID,domainName.toUpperCase());

                }

            }

        }

    }});

}

//the parameter ftID is the subtype identifier

private static void AddCodedPairsIntoMap(Domain domain,String layerID, String ftID, String domainName){

    Map<String, String> cvdPairs = ((CodedValueDomain) domain).getCodedValues();

    for (Map.Entry<String, String> entry : cvdPairs.entrySet()) {

        String dbValue = entry.getKey();

        String friendlyValue = entry.getValue();

        String key = layerID + ":" + ftID + ":" + domainName + ":" + dbValue;

        mCodedValues.put(key, friendlyValue);

    }

}

The last helper function, GetDomainValue is used to simply return the values that are stored in the coded value domains.

public static String GetDomainValue(String layerName,String subType, String fieldName, String dbValue){

    try {

        String requestedKey = layerName + ":" + subType + ":" + fieldName.toUpperCase() + ":" + dbValue;

        if(mCodedValues.containsKey(requestedKey)){

            Object retVal = mCodedValues.get(requestedKey);

            return  retVal.toString();

        }

        else{

            String defaultKey = layerName + ":-1:" + fieldName.toUpperCase() + ":" + dbValue;

            if(mCodedValues.containsKey(defaultKey)){

                Object retVal = mCodedValues.get(defaultKey);

                return  retVal.toString();

            }

            return  dbValue;

        }

    }

    catch (Exception ig){

        Log.e("BarCode|GetDomainValue",ig.getMessage());

        return dbValue;

    }

}

0 Kudos