Written by

Product Manager, Developer Experience at InterSystems
Question Raj Singh · Mar 1, 2023

ObjectScript ListOfObjects to 2D Python array

My general question is how to convert to a %Library.ListOfObjects to a Python "array-like" structure for use in Matplotlib.

Specifically, I have a Line Object which is comprised of a list of Points (see classes below). I want to pass the line to Python to create a Matplotlib Path.

Bonus points for converting the Point to a Python tuple!

Class geo.model.Point Extends %SerialObject
{
  Property latitude As %Float(MAXVAL = 90.0, MINVAL = -90.0, SCALE = 6);
  Property longitude As %Float(MAXVAL = 180.0, MINVAL = -180.0, SCALE = 6);
}
Class geo.model.Line Extends %SerialObject
{
  Property points As list Of geo.model.Point;
}
Product version: IRIS 2022.3

Comments

Eduard Lebedyuk · Mar 2, 2023

Easiest way would be to add %JSON.Adapter and use JSON for interop.

0
Raj Singh  Mar 9, 2023 to Eduard Lebedyuk

That would certainly work, but my use case is geographic data and a natural feature (e.g. a river or a coastline) could be composed of thousands of Points, so I want something more performance-oriented than JSON.

0
Eduard Lebedyuk  Mar 10, 2023 to Raj Singh

Project the list of geo.model.Point as a separate table:

Class geo.model.Line Extends%Persistent
{
Property points As list Of geo.model.Point(SQLPROJECTION = "table/column");
}

And you can use SQL query (via iris.sql) to get all points in line:

SELECT
    points_latitude, 
    points_longitude
FROM geo_model.Line_points
WHERE Line = ?
ORDERBY element_key

If you have thousands of points that would likely be the fastest way to transfer (barring callin/callout shenanigans).

0
Guillaume Rongier · Mar 2, 2023

have you tired this python module ?

pip3 install iris-dollar-list

it can convert $list to python list.

0
Raj Singh  Mar 9, 2023 to Guillaume Rongier

Hi @Guillaume Rongier I can't use that because my Points are floats and iris-dollar-list doesn't support floats yet?

0
Guillaume Rongier  Mar 10, 2023 to Raj Singh

Hi @Raj Singh, it does support float even is the readme is not up to date.

If you are looking for an objectscript / embedded python approach Alex Woodhead solution will fit you well.

If you are looking for an python only approach my module may help you.

0
Alex Woodhead · Mar 3, 2023

I made a utility for converting between Cache Lists and Arrays with Python Lists and Dictionaries.

Bi-directional. Ensures the keys are strings when going from Array keys to Dictionary keys.

https://github.com/alexatwoodhead/PyNow/blob/main/Py.Helper.xml

Also have an approach for storing (pickle) and retrieving Numpy arrays in IRIS Property Streams if that is of use.

0
Raj Singh · Mar 9, 2023

Using the the handy utility from @Alex Woodhead I was able to get something working. As of now you can get the Point, Line and Polygon code from https://github.com/isc-rsingh/spatialiris but I can't guarantee those classes won't change in the future. I include demo code here to show:

  1. $LIST can be converted to a Python list using Py.Helper
  2. 2-dimensional $LISTs (a $LIST of $LISTs) didn't work without running them through the invoke ClassMethod you see below. 
  3. you can integrate with matplotlib to take advantage of the broad and deep range of functionality offered by probably the most popular graphics library around
Class geo.ToolsExample Extends%RegisteredObject
{

ClassMethod createPolygon() As geo.model.Polygon
{
    setl = ##class(geo.model.Line).%New()
    dol.addPoint(##class(geo.model.Point).%New(0.01,0.01))
    dol.addPoint(##class(geo.model.Point).%New(2.01,0.01))
    dol.addPoint(##class(geo.model.Point).%New(2.01,2.01))
    dol.addPoint(##class(geo.model.Point).%New(0.01,2.01))
    dol.addPoint(##class(geo.model.Point).%New(0.01,0.01))
    set polygon = ##class(geo.model.Polygon).%New()
    set polygon.line = lReturn polygon
}

ClassMethod test()
{
    set polygon = ..createPolygon()
    set isin = ..PointInPolygon(polygon, ##class(geo.model.Point).%New(1.01,1.01))
    w isin,!
    set isout = ..PointInPolygon(polygon, ##class(geo.model.Point).%New(3.01,3.01))
    w isout,!
}

ClassMethod PointInPolygon(poly As geo.model.Polygon, pt As geo.model.Point) As%Boolean
{
    set mp = ##class(%SYS.Python).Import("matplotlib")
    set mpltPath = mp.path
    set polylist = poly.getAsList()
    set nothing = ##class(Py.Helper).toPyListOrString(polylist,.poly2)
    set point = ##class(Py.Helper).toPyListOrString(pt.getAsList())
    set path = ..invoke(mpltPath,poly2)
    Return path."contains_point"(point)
}

ClassMethod invoke(ByRef mpltPath As%SYS.Python, ByRef poly2) As%SYS.Python [ Language = python ]
{
    return mpltPath.Path(poly2)
}

}
0
Ben Spead  Mar 9, 2023 to Raj Singh

Well done @Raj Singh  - thank you for sharing your solution with the Community! 

0