Work order spatial query (Part 1) Part 1: General Cleanup

(Edit: Part 2 is here.)



I have a script in a Work Order Management System (Maximo) that performs a spatial query.

Details:


Takes the X&amp;Y coordinates of a work order in Maximo
Generates a URL from the coordinates (including other spatial query information)
Executes the URL via a separate script/library (LIB_HTTPCLIENT)
Performs a spatial query in an ESRI REST feature service (a separate GIS system)
JSON text is returned to Maximo with the attributes of the zone that the work order intersected
The zone number is parsed from the JSON text via a separate script/library (LIB_PARSE_JSON)
Inserts the zone number into the work order record




Jython main automation script

from psdi.mbo import MboConstants
from java.util import HashMap

#Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table via the SERVICEADDRESS system relationship
#The datatype of the LatitydeY and LongitudeX is decimal: 1234567.8335815760, 123456.4075621164
#Convert the decimals to integers, and then strings, for the purpose of generating the URL (I don't think the URL can have decimal places)
laty = str(mbo.getInt("SERVICEADDRESS.LatitudeY"))
longx = str(mbo.getInt("SERVICEADDRESS.LONGITUDEX"))

#Verify if the numbers are legitimate UTM coordinates
if len(laty) == 7 and len(longx) == 6:
    #Assemble the URL (including the longx and the laty). Note: The coordinates are flipped in the url
    url="http://something.com/arcgis/rest/services/something/Zones/MapServer/15/query?geometry=" + longx + "%2C" + laty + "&amp;geometryType=esriGeometryPoint&amp;spatialRel=esriSpatialRelIntersects&amp;outFields=*&amp;returnGeometry=false&amp;f=pjson"
    #Get the JSON text from the feature service (the JSON text contains the zone value)
    ctx = HashMap()
    ctx.put("url",url)
    service.invokeScript("LIB_HTTPCLIENT",ctx)
    json_text = str(ctx.get("response"))

    #Parse the zone value from the JSON text
    ctx = HashMap()
    ctx.put("json_text",json_text)
    service.invokeScript("LIB_PARSE_JSON",ctx)
    parsed_val = str(ctx.get("parsed_val"))

    #Enter the zone value into the zone field in the work order
    mbo.setValue("DESCRIPTION","Waste Zone: "+parsed_val,MboConstants.NOACCESSCHECK)


Jython library (LIB_HTTPCLIENT)

from psdi.iface.router import HTTPHandler
from java.util import HashMap
from java.lang import String
handler = HTTPHandler()
map = HashMap()
map.put("URL",url)
map.put("HTTPMETHOD","GET")
responseBytes = handler.invoke(map,None)
response = String(responseBytes,"utf-8")


JavaScript library (LIB_PARSE_JSON)

#The field name (ZONE) is hardcoded. I'm not sure if this is best practice or not.
var obj = JSON.parse(json_text);
parsed_val = obj.features[0].attributes.ZONE




How can this code be improved?
Functions are your friend

Even in Jython, functions are a valid construct. They allow for scope, so that you can better reason about temporary variables; give better stack traces when things go wrong; increase testability; etc. So you should move your code into some functions.

Invert your logic

This:

if len(laty) == 7 and len(longx) == 6:


should probably be inverted, and something done about it, i.e.

if len(laty) != 7 || len(longx) != 6:
    # throw, or at least print...
# continue on with the rest of the function


If you had requests...

then this:

url="http://something.com/arcgis/rest/services/something/Zones/MapServer/15/query?geometry=" + longx + "%2C" + laty + "&amp;geometryType=esriGeometryPoint&amp;spatialRel=esriSpatialRelIntersects&amp;outFields=*&amp;returnGeometry=false&amp;f=pjson"


could be fairly reduced in insanity. The query params can be formed as a dictionary passed to the get method.

PEP8

Use any kind of linter or modern IDE, and it will give you suggestions on how to reformat this thing to follow Python formatting standards (PEP8). The most obvious thing it will notice is lack of spaces after commas.

Otherwise - perhaps you should provide some context about why this thing exists in Jython.

Suggested

I don't know whether this will work, because I don't have your setup; so you'll probably have to tweak it.

from psdi.mbo import MboConstants
from java.util import HashMap
from urllib import urlencode
from urlparse import urlunparse, ParseResult


def get_coords():
    """
    Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table
    via the SERVICEADDRESS system relationship.
    The datatype of the LatitydeY and LongitudeX is decimal, i.e.
    1234567.8335815760, 123456.4075621164.
    """
    laty = mbo.getDouble("SERVICEADDRESS.LatitudeY")
    longx = mbo.getDouble("SERVICEADDRESS.LONGITUDEX")
    return laty, longx


def is_valid(laty, longx):
    """
    Verify if the numbers are legitimate UTM coordinates
    """
    return (0 &lt;= laty &lt;= 10e6 and
            167e3 &lt;= longx &lt;= 833e3)


def make_url(laty, longx):
    """
    Assemble the URL (including the longx and the laty). Note: The coordinates
    are flipped in the url
    """

    query = {
        'geometry': '%d,%d' % (laty, longx),
        'geometryType': 'esriGeometryPoint',
        'spatialRel': 'esriSpatialRelIntersects',
        'outFields': '*',  # You should narrow this if you only care about work zone.
        'returnGeometry': 'false',
        'f': 'pjson'
    }

    parts = ParseResult(scheme='http',
                        netloc='something.com',
                        path='/arcgis/rest/services/something/Zones/MapServer'
                             '/15/query',
                        query=urlencode(query),
                        fragment='')

    url = urlunparse(parts)
    return url


def fetch_waste_zone(url):
    # Get the JSON text from the feature service (the JSON text contains the 
    # zone value)
    ctx = HashMap()
    ctx.put("url", url)
    service.invokeScript("LIB_HTTPCLIENT", ctx)
    json_text = str(ctx.get("response"))

    # Parse the zone value from the JSON text
    ctx = HashMap()
    ctx.put("json_text", json_text)
    service.invokeScript("LIB_PARSE_JSON", ctx)
    parsed_val = str(ctx.get("parsed_val"))

    return parsed_val


def main():
    laty, longx = get_coords()
    if not is_valid(laty, longx):
        print('Invalid coordinates')
        return

    url = make_url(laty, longx)
    waste_zone = fetch_waste_zone(url)

    # Enter the zone value into the zone field in the work order
    mbo.setValue("DESCRIPTION", "Waste Zone: " + waste_zone,
                 MboConstants.NOACCESSCHECK)


main()


Note:


There are functions
The functions contain """docstrings""" at the top
You should preserve the UTM coordinates as doubles until it's necessary to format them as integers in the URL string
UTM coordinates have real limits that you should use - not just string length
Even if you don't have requests, you can still do somewhat saner query string formation via dictionary
If the coordinates are invalid, do something - maybe print, definitely quit