I've been working on the project mentioned last week, and found something interesting in ArcObjects for Java. It comes out looking like a bug, but it is just bad code that is hard to detect (partly because it involves setting a value a programmer would never expect to set).
The problem manifests itself in an error like this:
# A fatal error has been detected by the Java Runtime Environment:
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x60e9f090, pid=6184, tid=3704
# JRE version: Java(TM) SE Runtime Environment (8.0_05-b13) (build 1.8.0_05-b13)
# Java VM: Java HotSpot(TM) Client VM (25.5-b02 mixed mode windows-x86 )
# Problematic frame:
# C [GdbCoreLib.dll+0x14f090]
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
# An error report file with more information is saved as:
# C:\Users\arohne\workspace\GPS HHTS Analysis Esri Export\hs_err_pid6184.log
# If you would like to submit a bug report, please visit:
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
I poured over this for days, and even considered sending this to ESRI Support. As I continued looking into it, I found the problem:
fieldCount+=2; //FIXME: Trying to @&#$ things up
Previously, I didn't have the loop and instead had fieldsEdit.setFieldCount(2+GPSData.class.getDeclaredFields()+2);. The problem was that it was returning all the fields (both public and private), but I only defined public fields. This caused that error. I tested this by adding the fieldCount+=2; to the code (hence the FIXME tag) and was able to get things to work without intentionally changing the field count and break it when I have an incorrect field count.
I hope this helps someone out, as it isn't documented elsewhere that I could find.
After an unsuccessful attempt in trading mode choice calibration with bugfixing in Cube, I ended up figuring out several things about Voyager PT and route trace files.
The trace reports are files that describe the transit routes from one zone to another. This is a little more detailed than the skim matrices, as the skims don't tell you which routes are used or the operator of the routes (since there are five transit providers in the OKI model, this is a major concern). The trace reports look like this (this is from a subset of the OKI model):
There is one thing that's not really well documented in Cube Help, and that is how to generate the trace reports. The one thing that has to be done is on the FILEI ROUTEI or FILEO ROUTEO lines, the line must include both REPORTI=z-Z and REPORTO=z-Z. The report will not generate if only one side is there - both keys have to be there. There must also be a FILEO REPORTO file, but that's required for any Voyager PT run.
Having this was only half the battle. I needed a matrix of the operator, and for the entire 3,200 (ish) zone model run, the resulting report file is over 1.2 GB (and that doesn't include Dayton's transit!). So I had to go through this file quickly. Enter Java.
I wrote a quick (both in code and in running) Java program to read the report file and a DBF of the lines (which I got from the geodatabase route layer). The program sorts through the report file looking for the excerpts above, stores them in memory, and then scans through them to output a dbf with the from-zone (I), to-zone (J), and the operator. This is multi-threaded, so on my 8-core beast, it runs very fast.
I haven't worked in R in several days because I've been working on a new project that will assist with getting good transit speed curves from highway data. The project is in Java and is on my Github page.
I'm working on making part of it multi-threaded, which is new to me.
A second project that is still in my mind (and I'm not sure if this will become part of this one or another separate project) will be to use transit GPS traces to get good trip length frequencies on transit.
I stumbled on a problem that seems to have no easy answer. Working on the count stations layer here at the office, I found that we had a small number of points that weren't located in the GIS feature class, although we DO have X and Y coordinates for them.
Since searching on Google turned up nothing, I wrote my own solution. Since I already had some Java code to look for selected features and get to the actual features, I copied that code into a new project and made a few modifications. Those modifications are posted on Github. Even better, I actually used a few comments in this one! 🙂
As a follow-up to my prior post, this is how to use the Cube Voyager API to read a path file. I highly recommend you read the other article first, as it talks more about what is going on here.
The interface for the path reader is larger because of the return structure. The code below includes the interfaces to the DLL calls and the structure for the path data returned by some of them. Note that I didn't do PathReaderReadDirect. It doesn't seem to work (or I'm not trying hard enough).
Once the interface is in place, the code is reasonably simple. However, I'm seeing a few interesting things in the costs and volumes in both C++ and in Java, so I wouldn't use those values. I guess if you need to determine the costs, you should save the costs with the loaded highway network to a DBF file and read that into an array that can be used to store and get the values.
The Final Word... For Now
Java is a great programming language. Using these DLLs can help you do some interesting stuff. However, it seems that there are very few people using the API, which is concerning. I personally would like to see an interface for reading .NET files and writing matrices. But I can't expect Citilabs to put time in on that when it seems there are so few people using it.
I've begun to really enjoy Java. It's hot, black exterior exposes a sweet bitterness that matches few other things in this world. Oh, wait, this is supposed to be about the other Java - the programming language!
The "Holy Grail" of API programming with Cube Voyager to me has been using the API in Java. I can program in C++ quite well, but I have a staff that can't. We're likely going to be going to a Java based modeling structure in the next few years, so it makes sense to write everything in Java and keep the model down to two languages - Cube Voyager and Java.
Setting up the Java Environment
There are three things to do to setup the Java environment to make this work. The first is to place the Cube DLL in the right location. The second is to get JNA and locate the libraries to where you need them. The final is to setup the Java execution environment.
First, copy the VoyagerFileAccess.dll file (and probably it's associated lib file) to C:\Windows. It should work. I'm using a Windows 7-64 bit machine, so if it doesn't work, try C:\Windows\System32 and C:\Windows\System.
Second, get JNA. This allows the Java compiler to connect to the DLL. The latest version can be downloaded from Github (go down to "Downloads" under the Readme.md... just scroll down 'till you see it, and get both platform.jar and jna.jar).
If you're on a 64-bit computer, the second thing to do is to set your jdk environment to use a 32-bit compiler. I use Eclipse as my IDE, so this is done through the project properties. One location is the Java Build Path - on the Libraries tab, make sure the JRE System Library is set to a 32-bit compiler. In the Java Build Path screenshot below, you can see that all the locations are in C:\Program Files (x86) - this is an easy (although not foolproof) way to show that this is a 32-bit compiler.
While you're setting up stuff in this window, make sure the jna.jar and platform.jar are linked here as well (click "Add External JARs..." and locate those two files).
Another place to check in Eclipse is the Java Compiler settings, which should have "Use Compliance from execution environment..." checked.
The thing that makes this work is this part of the programming. You can see in this that I create an interface t0 the Voyager DLL file by loading the DLL, and then setup some pointer objects to hold the memory pointer variable (the "state" variable in all of these) and set up the functions to read from the matrix.
The next part that makes this work is the actual programming. In the code below, the first thing I do is define vdll as an instance of the voyagerDLL interface. Then, I open a matrix file (yes, it is hard-coded, but this is an example!), get the number of matrices, zones, the names, and I start reading the matrix (in the for loops). I only print every 100th value, as printing each one makes this slow a bit. The actual reading is quite fast. Finally, I close the matrix and the program terminates.
The big issue I noticed is that if the matrix is not found, the Pointer variable returned by MatReaderOpen will be null, but nothing will be in the error value. I've tried redefining the error value to be a string in the interface, but it does the same thing. However, I don't recall if it did anything in C++. At any rate, there needs to be some error checking after the matrix is opened to ensure that it actually has opened, else the program will crash (and it doesn't do a normal crash).