Extending MV Basic by Calling Python

Introduction

One of the compelling things about using Python is the ability to extend the capability of the MultiValue platform. Perhaps you are thinking that means Python having access into the MultiValue environment to read / write data files, run commands and call Basic subroutines. Certainly that can be done but more than that, Python programs can be called from within the MV environment.

I call this ‘bi directional’ connectivity: Python can call into the MV environment or the MV environment can call outside to run Python programs. In this blog post, I’ll build an example of how MV Basic can be extended by calling out and running a Python function.

Calculating Distances

OK, this example doesn’t actually need Python as it could be done with MV Basic. However, it’s an interesting sample that is not too trivial but not complicated either. So, on that basis it serves to show how to call Python for those situations where it can offer real world assistance by extending MV Basic.

What we will do is calculate the distance between two points on earth using a function that implements the Haversine formula for calculating distances on a sphere.

Python Function

Here is the source code of the Python function (distance_between_points) that does the distance calculation: 

import math
from decimal import Decimal
def distance_between_points(lat1, long1, lat2, long2):
lat1 = Decimal(lat1)
lat2 = Decimal(lat2)
long1 = Decimal(long1)
long2 = Decimal(long2)
# Convert latitude and longitude to
# spherical coordinates in radians.
radius = 6378 # kilometres or 3960 miles
dlat = math.radians(lat2-lat1)
dlon = math.radians(long2-long1)
a = math.sin(dlat/2)*math.sin(dlat/2) + math.cos(math.radians(lat1)) \
* math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = radius * c
return d

Note: I did not write the function distance_between_points displayed above but rather I adapted it from the DISTANCEBTWNPOINTS.PY program in the PP file of the XDEMO account, which comes with Universe 11.3.1.

This is written in Python, so it may look a little unfamiliar but hopefully you can read it well enough. One of the things I like about Python is that the syntax is simple and easy to read.

To help read it, comments in Python start with the hash (#) character https://docs.python.org/3/tutorial/introduction.html.

As you can see, the function takes four parameters, being the latitude and longitude for each of the two points and it returns the distance between those points in kilometres.

Testing the Function

Let’s test our function by measuring the distance between the Australian cities of Sydney and Melbourne. I used the site https://www.gps-coordinates.net/ to get the GPS co-ordinates for the Sydney Opera House and Federation Square in Melbourne, so we will measure the distance between those two points:

Sydney Opera House: Bennelong Point, Sydney NSW 2000, Australia
Latitude: -33.856784
Longitude: 151.215297

Federation Square: Swanston St & Flinders St, Melbourne VIC 3000, Australia
Latitude: -37.817979
Longitude: 144.969058

The function is in a module called DistanceCalcs (saved in a file called DistanceCalcs.py) and I’ll import that module to make the distance_between_points function available.

First, let’s test it from Python. The following output shows two things:

  • First, the DistanceCalcs module is imported and given the identifier of dc. This is done with the Python import statement.
  • Second, we call the distance_between_points function, append the result to a string and output the string using the Python print function.

Here is the test:

import DistanceCalcs as dc
print('The distance is: ',dc.distance_between_points(-33.856784, 151.215297, -37.817979, 144.969058))
The distance is:  715.3571924529327

The function is telling us that it is 715 km from Sydney to Melbourne but if you have driven between those two cities you’ll know that it is a lot further than that! The difference is that this function is calculating the distance in a straight line whereas the road, of course, meanders around a bit.

Extending MV Basic

We now know that the function works, so let’s use this Python function to extend MV Basic. The Python API for MV Basic includes several functions for interacting with Python and one of those is the PYCALLFUNCTION function, which gives the ability to call a Python function from MV Basic. That is just what we want to do so let’s look at the syntax of the function: 

pyresult = PyCallFunction(moduleName, functionName[,arg1,arg2,...])
Where:
pyresult is a standard U2 BASIC variable or a PYOBJECT variable.
moduleName is the name of the module where the function is defined.
functionName is the name of the function to be called.
arg1,arg2,… are the arguments to the function object that can be evaluated to a string, a number, or a PYOBJECT.

For more information about the Python API for MVBasic, please refer to the Rocket U2 Python User Guide which is available under the Rocket U2 section of the Rocket Software documentation site.

I’ll use the PyCallFunction function in a small MV Basic program that will ask for the IDs of two cities and then display the distance between those cities as calculated by the Python function.

The GPS co-ordinates for the cities are held in a UniVerse file called CITY, so here is a listing of that file:

CITY…… City…… Latitude.. Longitude.

CAN Canberra -35.30654 149.1266
HOB Hobart -42.87936 147.32941
PER Perth -31.95264 115.8574
BNE Brisbane -27.47126 153.0238
SYD Sydney -33.8568 151.2153
ADL Adelaide -34.9621 138.5999
MEL Melbourne -37.8180 144.9691
DAR Darwin -12.411153 130.877550

Here is the MV Basic program listing of the program CALCDISTANCE. Note the line where we call the Python function using the PYCALLFUNCTION function.

MODULE = 'DistanceCalcs'
FUNC = 'distance_between_points'
C1 = ''
C2 = ''
PROMPT ''
PRINT 'What is city 1? ':
INPUT C1
PRINT 'What is city 2? ':
INPUT C2
OPEN 'CITY' TO F.CITY ELSE RESULT = '' ; RETURN
READ C1REC FROM F.CITY, C1 THEN
C1NAME = C1REC
C1LAT = C1REC
C1LONG = C1REC
END ELSE
ERRMSG = 'CITY ID IS NOT AVAILABLE'
GOTO ERRORGETDISTANCE
END
READ C2REC FROM F.CITY, C2 THEN
C2NAME = C2REC
C2LAT = C2REC
C2LONG = C2REC
END ELSE
ERRMSG = 'CITY ID IS NOT AVAILABLE'
GOTO ERRORGETDISTANCE
END
PYDIST = PYCALLFUNCTION(MODULE, FUNC, C1LAT, C1LONG, C2LAT, C2LONG)
CRT 'Distance from ':C1NAME:' to ':C2NAME:': ':OCONV(PYDIST, 'MD1P')
EXITGETDISTANCE:
CLOSE F.CITY
STOP
ERRORGETDISTANCE:
CRT ERRMSG
GOTO EXITGETDISTANCE
END

Now we need to run the program and see what it does, so here again is the calculation of the distance between Sydney and Melbourne but this time using MV Basic and having that call the Python function:

RUN PBP CALCDISTANCE
What is city 1? SYD
What is city 2? MEL
Distance from Sydney to Melbourne: 715.4 

As before, it calculates the distance to be 715 kilometres.

Conclusion

This has been a short introduction to using Python to extend the ability of MV Basic. There is a lot more to do – particularly when we start using some of the many Python packages that are available. The logical next step from here is to show how to use Python packages and specifically how to use them to extend the functionality of the MultiValue platforms from Rocket Software but that’s for another day.

The sample shown here only includes that one Python function (distance_between_points), one MV Basic program (CALCDISTANCE) and the CITY file. I have shown them all here, so please feel free to try this out for yourself. Send me an email (ross.reichardt@mbs.net.au) and let me know how you get on – I’d love to hear from you!

Ross Reichardt

Ross Reichardt 3 Posts

I'm an account manager for Meier Business Systems (MBS) and have been working in MultiValue since late 1999. Most of that time has been with the D3 platform but I now enjoy working with U2 as well as D3. I have a strong interest in modernisation, web technology, web services and now Python. Away from work I love talking about cars - mainly old British sports cars. So drop me a line and let's chat about MultiValue, modernisation technologies and Austin Healeys!

0 Comments

Leave a Comment

Your email address will not be published. Required fields are marked *