Charlie Davies
Charlie Davies

Reputation: 1834

jruby adding a java library

I am attempting to run http://www.savarese.com/software/libssrckdtree-j/ inside a Jruby file.

my code looks something like this:

require 'java'
require 'libssrckdtree-j-1.0.2.jar'

GenericPoint = Java::com.savarese.spatial.GenericPoint
KDTree = Java::com.savarese.spatial.KDTree
NearestNeighbors = Java::com.savarese.spatial.NearestNeighbors

tree = KDTree.new

def generate_points(tree, size)
  size.times do
    x = rand(100)
    y = rand(100)
    point = GenericPoint.new(x, y)
    tree.put(point, point.to_s)
  end
end

generate_points(tree, 20000)


nearest = NearestNeighbors.new()
point = [1,1]

nearest.get(tree, point, 1, false)

However when I run this code I get

NameError: no method 'get' for arguments (com.savarese.spatial.KDTree,org.jruby.RubyArray,org.jruby.RubyFixnum,org.jruby.RubyBoolean) on Java::ComSavareseSpatial::NearestNeighbors
  (root) at kdjava.rb:25

Which I dont get as the documentation for the library says to use get, and i use put on tree for the KDtree and it worked.

What I am trying to do is construct a series of lat lngs into a kd tree using this library and then query another set of lat lngs to see their nearest neighbor inside the KD tree.

I am okay at Ruby - but I suck at Java. I am using Jruby to take advantage of these faster libraries.

Upvotes: 1

Views: 735

Answers (1)

Patrick
Patrick

Reputation: 5994

The api documentation for NearestNeighbor#get() indicates that the method takes four arguments of types KDTree, P, int, boolean, which are all Java types.

Your error message indicates that you passed in KDTree, RubyArray, RubyFixnum, RubyBoolean. So, KDTree looks ok. I believe JRuby will automatically coerce Fixnum and Boolean to the appropriate Java types. That leaves the second arg where you are passing RubyArray where the method expects P.

The documentation of NearestNeighbors indicates that P is a generic type, P extends Point<Coord>. Point is an interface. GenericPoint implements that interface. So instead of passing [1,1], try passing Java::com.savarese.spatial.GenericPoint.new(1,1)

edit:

Looking a bit further, GenericPoint must be created with Coord, another generic type Coord extends java.lang.Comparable<? super Coord>. In Java, there are two kinds of ints, the primitive type int (fast), and the "boxed" object java.lang.Integer. Integer implements the Comparable interface, which is required by GenericPoint. I did not get any errors running the following:

require 'java'
require 'libssrckdtree-j-1.0.2.jar'

GenericPoint = Java::com.savarese.spatial.GenericPoint
KDTree = Java::com.savarese.spatial.KDTree
NearestNeighbors = Java::com.savarese.spatial.NearestNeighbors

def box(i)
  java.lang.Integer.new(i)
end

tree = KDTree.new

def generate_points(tree, size)
  size.times do
    x = box(rand(100))
    y = box(rand(100))
    point = GenericPoint.new(x, y)
    tree.put(point, point.to_s)
  end
end

generate_points(tree, 20000)


nearest = NearestNeighbors.new()
point = GenericPoint.new(box(1), box(1))

nearest.get(tree, point, 1, false)

A note about the above, normally in Java to create a boxed Integer, one uses java.lang.Integer.valueOf(int). For reasons beyond me, this resulted in the same error one gets when using int: NativeException: java.lang.ClassCastException: org.jruby.RubyFixnum cannot be cast to java.lang.Number. So I instead used the constructor, java.lang.Integer.new.

Upvotes: 2

Related Questions