Rounding up Scale to set intervals with Python

1274
9
Jump to solution
08-30-2018 12:16 AM
LindsayRaabe2
New Contributor II

So I have a python script that sets a dataframe extent to the extent of a particular layer and currently the user would then set the scale to something neater (i.e. 1:4,387 would become 1:5,000). I have seen ways to round up to a set increment (i.e. every 1000) but I am more interested in getting it to round up to the next interval in a list of intervals (i.e. 5000, 7500, 10000, 12500, 20000, 25000, 30000, 40000...). Can anyone recommend a way to do this?

 

Demo code;

# input parameters;
Plantation = arcpy.GetParameter(0)

 

# variables;

mxd = mapping.MapDocument("CURRENT")

layers = mapping.ListLayers(mxd)

dflist = arcpy.mapping.ListDataFrames(mxd, "")

new_exp = "PLANTATION = '" + Plantation + "'"

 

for lyr in layers:

   if lyr.name == "Mapping Patch":

         lyr.definitionQuery = new_exp
         arcpy.AddMessage(lyr.name + " definition query updated")
         ext = lyr.getExtent() #gets the new extent of the Fertilser Plan
         dflist[0].extent = ext #applies the Fertiliser Plan extent to the dataframe

0 Kudos
1 Solution

Accepted Solutions
DanPatterson_Retired
MVP Emeritus

provide the list as a variable and select it wherever you are getting the input value.  But I can't tell where you are looping to get the extent.

Perhaps if you throw some print statements in to see what is working now.

There is no 'rounding' but I think you want to select the next highest in a list.  unfortunately lists don't really have a 'position' option, so you have to use the bisect module in your script...

Have a read, and see if this is what you are after

from bisect import bisect

a = [5000, 7500, 10000, 12500, 20000, 25000, 30000, 40000]
scale = 13000

a[bisect(a, scale)]

Out[21]: 20000  # 'rounded up' to the next scale

View solution in original post

9 Replies
DanPatterson_Retired
MVP Emeritus

provide the list as a variable and select it wherever you are getting the input value.  But I can't tell where you are looping to get the extent.

Perhaps if you throw some print statements in to see what is working now.

There is no 'rounding' but I think you want to select the next highest in a list.  unfortunately lists don't really have a 'position' option, so you have to use the bisect module in your script...

Have a read, and see if this is what you are after

from bisect import bisect

a = [5000, 7500, 10000, 12500, 20000, 25000, 30000, 40000]
scale = 13000

a[bisect(a, scale)]

Out[21]: 20000  # 'rounded up' to the next scale
MichaelVolz
Esteemed Contributor

Thank you for providing this information as it can be useful in a replacement app for a .NET app that will be getting retired with the migration to Pro.

0 Kudos
DanPatterson_Retired
MVP Emeritus

Michael... there are lots of hidden gems in python

LindsayRaabe2
New Contributor II

Hi Dan. Thanks for the tip - trying to integrate it now. Is there a typo in the last line though? 

Is - Out[21]: 20000  # 'rounded up' to the next scale
Should be? - Out[1]: 20000  # 'rounded up' to the next scale
0 Kudos
DanPatterson_Retired
MVP Emeritus

Lindsay

Out[21]: is simply from Spyder's IPython built in console.  It just keeps track of line numbers and I didn't remove it as I usually do to avoid confusion (failed miserably though )

The answer is simply … 20000 ...

LindsayRaabe2
New Contributor II

Here's my full original code. 

I'm thinking that I need to add your code in after Line 52 of my code (original in full at bottom). After setting the extent of the dataframe, I'm trying to get the current scale, run it though your code snippet to select the next value above the existing scale and apply the output to the dataframe. This same logic would then be applied to other parts of the code that do the same thing. I think I'm getting hung up on how I get both the existing scale and the output of your code snippet as a variable to use as the final input. 

import arcpy
from arcpy import env, mapping
from bisect import bisect

#input parameters;
Plantation = arcpy.GetParameter(0)
ForestID = arcpy.GetParameter(1)
CommonName = arcpy.GetParameter(2)
PYear = arcpy.GetParameter(3)

#variables;
title1 = Plantation + " PLANTATION"
title2 = Plantation + "\r\nPLANTATION"
title3 = Plantation + " (" + CommonName + ")"
title4 = Plantation + " P" + PYear + " AREA STATEMENT"
mxd = mapping.MapDocument("CURRENT")
elements = mapping.ListLayoutElements(mxd)
layers = mapping.ListLayers(mxd)
dflist = arcpy.mapping.ListDataFrames(mxd, "")
new_exp = "PLANTATION = '" + Plantation + "'"
new_sur_exp = "PLANTATION <> '" + Plantation + "'"
as_new_exp = "EnteredInGeoMaster IS NOT NULL AND Plantation = '" + Plantation + "'"
scalelist = [5000, 7500, 10000, 12500, 20000, 25000, 30000, 40000, 50000, 60000, 70000, 75000, 80000, 90000, 100000]

...
        
     elif lyr.name == "PlantationsHarvestPlan_OperationsPlanner_WA":
          lyr.definitionQuery = new_exp
          arcpy.AddMessage(lyr.name + " definition query updated")
          ext = lyr.getExtent() #gets the new extent of the Harvest Plan
          dflist[0].extent = ext #applies the Harvest Plan extent to the dataframe
          oldscale = dflist[0].getScale # Get Scale here
          scalelist[bisect(scalelist, oldscale)]# Run bisect here
          dflist[0].scale = ????? # Use output of bisect to set new scale

...‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Original code in full (works in this format but doesn't round/increment scales);

import arcpy
from arcpy import env, mapping

#input parameters;
Plantation = arcpy.GetParameter(0)
ForestID = arcpy.GetParameter(1)
CommonName = arcpy.GetParameter(2)
PYear = arcpy.GetParameter(3)

#variables;
title1 = Plantation + " PLANTATION"
title2 = Plantation + "\r\nPLANTATION"
title3 = Plantation + " (" + CommonName + ")"
title4 = Plantation + " P" + PYear + " AREA STATEMENT"
mxd = mapping.MapDocument("CURRENT")
elements = mapping.ListLayoutElements(mxd)
layers = mapping.ListLayers(mxd)
dflist = arcpy.mapping.ListDataFrames(mxd, "")
new_exp = "PLANTATION = '" + Plantation + "'"
new_sur_exp = "PLANTATION <> '" + Plantation + "'"
as_new_exp = "EnteredInGeoMaster IS NOT NULL AND Plantation = '" + Plantation + "'"

def ChangeTextElement(mxd,textElementName,newText):
    textToChange = mapping.ListLayoutElements(mxd,"TEXT_ELEMENT",textElementName)[0]
    textToChange.text = newText

for elm in elements:
    if elm.name == "PTNID":
        ChangeTextElement(mxd,"PTNID",ForestID.upper())

    elif elm.name == "Plantation Name 1 line":
        ChangeTextElement(mxd,"Plantation Name 1 line",title1.upper())

    elif elm.name == "Plantation Name 2 lines":
        ChangeTextElement(mxd,"Plantation Name 2 lines",title2.upper())

    elif elm.name == "SF Plantation Name":
        ChangeTextElement(mxd,"SF Plantation Name",title3.upper())
        
    elif elm.name == "Area Statement Title":
        ChangeTextElement(mxd,"Area Statement Title",title4.upper())
    
for lyr in layers:
    if lyr.name == "OpCode Boundaries":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        
    elif lyr.name == "PlantationsHarvestPlan_OperationsPlanner_WA":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Harvest Plan
        dflist[0].extent = ext #applies the Harvest Plan extent to the dataframe

    elif lyr.name == "Mapping Patch":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Fertilser Plan
        dflist[0].extent = ext #applies the Fertiliser Plan extent to the dataframe

    elif lyr.name == "Mapping Patch Surround":
        lyr.definitionQuery = new_sur_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        
    elif lyr.name == "Planned Establishment Areas":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Fertilser Plan
        dflist[0].extent = ext #applies the Fertiliser Plan extent to the dataframe

    elif lyr.name == "Target Fertiliser Areas":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Fertilser Plan
        dflist[0].extent = ext #applies the Fertiliser Plan extent to the dataframe

    elif lyr.name == "Captured Establishment Areas":
        lyr.definitionQuery = as_new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Captured Establishment Areas layer
        dflist[0].extent = ext #applies the Captured Establishment Areas layer extent to the dataframe

    elif lyr.name == "Mapping Focus Locality":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Mapping Focus Locality
        dflist[1].extent = ext #applies the Mapping Focus Locality extent to the dataframe
        dflist[2].extent = ext #applies the Mapping Focus Locality extent to the dataframe
        dflist[1].scale = 500000 # sets the scale to 1:500,000
        dflist[2].scale = 1500000 # sets the scale to 1:1,500,000

    elif lyr.name == "Mapping Surround Locality":
        lyr.definitionQuery = new_sur_exp
        arcpy.AddMessage(lyr.name + " definition query updated")       ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
DanPatterson_Retired
MVP Emeritus

woooo, that is a

a[bisect(a, scale)] # the simple expression

scalelist = [5000, 7500, 10000, 12500, 20000, 25000, 30000, 40000, 50000, 60000, 70000, 75000, 80000, 90000, 100000]

scale = ???      # you can't get scale from the extent, not sure where you are getting it, but it needs to be an integer

will become

new_scale = scalelist[bisect(scalelist, scale)]   # bisect returns a 'slice' of the list... that is a position, then it returns the right-most value at that position

where are you getting scale then?

LindsayRaabe2
New Contributor II

Awesome! Figured it out. Snippet of final code below. Thank you for your help Dan Patterson‌. Been thinking of this for a while. Good to be able to do it. (Slowly improving my python skills!). 

    elif lyr.name == "PlantationsHarvestPlan_OperationsPlanner_WA":
        lyr.definitionQuery = new_exp
        arcpy.AddMessage(lyr.name + " definition query updated")
        ext = lyr.getExtent() #gets the new extent of the Harvest Plan
        dflist[0].extent = ext #applies the Harvest Plan extent to the dataframe
        oldscale = dflist[0].scale # Get scale of dataframe
        newscale = scalelist[bisect(scalelist, oldscale)] # bisect old scale to get new scale
        dflist[0].scale = newscale # Apply new scale to dataframe
DanPatterson_Retired
MVP Emeritus

Glad it worked out Lindsay!

0 Kudos