<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ojwiki.soldin.de/index.php?action=history&amp;feed=atom&amp;title=How_to_write_a_new_driver</id>
	<title>How to write a new driver - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ojwiki.soldin.de/index.php?action=history&amp;feed=atom&amp;title=How_to_write_a_new_driver"/>
	<link rel="alternate" type="text/html" href="https://ojwiki.soldin.de/index.php?title=How_to_write_a_new_driver&amp;action=history"/>
	<updated>2026-04-21T23:08:31Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://ojwiki.soldin.de/index.php?title=How_to_write_a_new_driver&amp;diff=337&amp;oldid=prev</id>
		<title>Mentaer: Created page with &#039;Here are some explanations for developers who need to write a driver to &lt;b&gt;read&lt;/b&gt;/write a new &lt;b&gt;file&lt;/b&gt; format.  == Old api vs new api ==  There is an old api in com.vividsol…&#039;</title>
		<link rel="alternate" type="text/html" href="https://ojwiki.soldin.de/index.php?title=How_to_write_a_new_driver&amp;diff=337&amp;oldid=prev"/>
		<updated>2009-10-18T22:25:31Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;#039;Here are some explanations for developers who need to write a driver to &amp;lt;b&amp;gt;read&amp;lt;/b&amp;gt;/write a new &amp;lt;b&amp;gt;file&amp;lt;/b&amp;gt; format.  == Old api vs new api ==  There is an old api in com.vividsol…&amp;#039;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Here are some explanations for developers who need to write a driver to &amp;lt;b&amp;gt;read&amp;lt;/b&amp;gt;/write a new &amp;lt;b&amp;gt;file&amp;lt;/b&amp;gt; format.&lt;br /&gt;
&lt;br /&gt;
== Old api vs new api ==&lt;br /&gt;
&lt;br /&gt;
There is an old api in com.vividsolutions.jump.io, with the JUMPReader and JUMPWriter classes, but developpers are encouraged to use new new api, with two main classes :&lt;br /&gt;
&lt;br /&gt;
* com.vividsolutions.jump.io.datasource.DataSource &lt;br /&gt;
* com.vividsolutions.jump.io.datasource.Connection &lt;br /&gt;
&lt;br /&gt;
== DataSource or DataStore? ==&lt;br /&gt;
* &amp;#039;&amp;#039;com.vividsolutions.jump.io.datasource&amp;#039;&amp;#039; package is for file data sources, while &lt;br /&gt;
* &amp;#039;&amp;#039;com.vividsolutions.jump.datastore&amp;#039;&amp;#039; is for database access.&lt;br /&gt;
&lt;br /&gt;
== Datasource related classes are spread across several packages ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1 - The driver classes&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In com.vividsolutions.jump.io.datasource package, you will find the main classes to write a driver :&lt;br /&gt;
&lt;br /&gt;
* DataSource will store the datasource properties (ex. file name) and return a Connection to this DataSource&lt;br /&gt;
&lt;br /&gt;
* Connection interface which has to be implemented by a class which will have the responsability to return FeatureCollections or to execute updates&lt;br /&gt;
&lt;br /&gt;
* ReaderWriterFileDataSource is a special DataSource created from a JUMPReader (and a JUMPWriter). It is used as a bridge between the old and the new api.&lt;br /&gt;
&lt;br /&gt;
* DataSourceQuery : a small wrapper including a string representing a query and the source to apply it against.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2 - The UI stuff&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
&lt;br /&gt;
com.vividsolutions.jump.workbench.datasource contains all the UI elements :&lt;br /&gt;
&lt;br /&gt;
* The DataSourceQueryChooserDialog with its getCurrentChooser method returning an implementation of DataSourceQueryChooser.&lt;br /&gt;
&lt;br /&gt;
* DataSourceQueryChooser is the UI for picking datasets for a given format. It produces DataSourceQueries  each of which encapsulates a query string and the DataSource to run it against. A partial implementation is FileDataSourceQueryChooser which has two subclasses, one for file loading (LoadFileDataSourceQueryChooser) and the other one for file saving (SaveFileDataSourceQueryChooser)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3 - PlugIns&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The plugins related to driver installation are also in com.vividsolutions.jump.workbench.datasource package.&lt;br /&gt;
&lt;br /&gt;
* There is a plugin for the installation of all the standard drivers called InstallStandardDataSourceQueryChooserPlugIn.&lt;br /&gt;
&lt;br /&gt;
* There is a hierarchy of plugins which parent is AbstractLoadSaveDatasetPlugIn and which concrete implementations are LoadDatasetFromFilePlugIn and SaveDatasetAsFilePlugIn&lt;br /&gt;
&lt;br /&gt;
== And now let&amp;#039;s write our first driver ==&lt;br /&gt;
In this paragraph, we&amp;#039;ll write a very simple driver to load xyz data files. Main characteristics of the file format and the parser characteristics are as follows :&lt;br /&gt;
&lt;br /&gt;
* each line represents a point,&lt;br /&gt;
&lt;br /&gt;
* each line contains fields separated by a whitespace, a tabulation, a comma, a semi-column,&lt;br /&gt;
&lt;br /&gt;
* the x field is in one of the 6 first columns (always the same clumn),&lt;br /&gt;
&lt;br /&gt;
* the y field follows the x field&lt;br /&gt;
&lt;br /&gt;
* the line contains an optional z field&lt;br /&gt;
&lt;br /&gt;
* every field preceding the x field is kept as a string attribute,&lt;br /&gt;
&lt;br /&gt;
* every field following the y or the z field is ignored&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1 - The driver installer PlugIn&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import java.io.File;&lt;br /&gt;
import java.util.Map;&lt;br /&gt;
import java.util.HashMap;&lt;br /&gt;
import javax.swing.JLabel;&lt;br /&gt;
import javax.swing.JPanel;&lt;br /&gt;
import javax.swing.JComboBox;&lt;br /&gt;
import com.vividsolutions.jump.workbench.datasource.*;&lt;br /&gt;
import com.vividsolutions.jump.util.Blackboard;&lt;br /&gt;
import com.vividsolutions.jump.workbench.plugin.PlugInContext;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * PlugIn installing a driver to read XYZ data files.&lt;br /&gt;
 * @author Michaël Michaud&lt;br /&gt;
 * @version 0.1 (2007-04-29)&lt;br /&gt;
 */&lt;br /&gt;
public class InstallXYZDataSourceQueryChooserPlugIn extends&lt;br /&gt;
             InstallStandardDataSourceQueryChoosersPlugIn {&lt;br /&gt;
   &lt;br /&gt;
   public static final String INDEX = &amp;quot;INDEX&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /**&lt;br /&gt;
    * PlugIn initialization.&lt;br /&gt;
    */&lt;br /&gt;
    public void initialize(final PlugInContext context) throws Exception {&lt;br /&gt;
        Blackboard blackboard = context.getWorkbenchContext().getWorkbench().getBlackboard();&lt;br /&gt;
        final String description = &amp;quot;XYZ (plain text)&amp;quot;;&lt;br /&gt;
        final JComboBox jcb = new JComboBox(new Object[]{&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;});&lt;br /&gt;
        &lt;br /&gt;
        DataSourceQueryChooserManager.get(blackboard).addLoadDataSourceQueryChooser(&lt;br /&gt;
            new LoadFileDataSourceQueryChooser(XYZDataSource.class,&lt;br /&gt;
                                               description,&lt;br /&gt;
                                               new String[] { &amp;quot;&amp;quot;, &amp;quot;xyz&amp;quot;, &amp;quot;txt&amp;quot;, &amp;quot;asc&amp;quot; },&lt;br /&gt;
                                               context.getWorkbenchContext()) {&lt;br /&gt;
                &lt;br /&gt;
                // Put the x column index in the properties map&lt;br /&gt;
                protected Map toProperties(File file) {&lt;br /&gt;
                    Map properties = new HashMap(super.toProperties(file));&lt;br /&gt;
                    JPanel panel = getSouthComponent1();&lt;br /&gt;
                    properties.put(INDEX, ((JComboBox)panel.getComponent(1)).getSelectedItem().toString());&lt;br /&gt;
                    return properties;&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                // Create a component to select the comlumn index of xyz data&lt;br /&gt;
                protected JPanel getSouthComponent1() {&lt;br /&gt;
                    JPanel southComponent = new JPanel();&lt;br /&gt;
                    southComponent.add(new JLabel(&amp;quot;Index of X column&amp;quot;));&lt;br /&gt;
                    southComponent.add(jcb);&lt;br /&gt;
                    return southComponent;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This class extends InstallStandardDataSourceQueryChoosersPlugIn, which is used to install shapefile driver, gml driver...&lt;br /&gt;
&lt;br /&gt;
InstallXYZDataSourceQueryChooserPlugIn adds a special widget to the interface to select the index of the X column (the Y and Z columns are supposed to be just after the X column).&lt;br /&gt;
&lt;br /&gt;
The widget is a final JComboBox initialized during the installer initialization, and is made visible in the southComponent of the file chooser dialog.&lt;br /&gt;
&lt;br /&gt;
The toProperties method of the LoadFileDataSourceQueryChooser put this column index in the properties Map used by the special XYZDataSource.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2 - The XYZ DataSource&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now, let&amp;#039;s write the class doing the job. XYZDataSource extends DataSource and has to return an implementation of the Connection interface.&lt;br /&gt;
&lt;br /&gt;
For a read-only driver as our XYZ driver, the Connection method to averload is :&lt;br /&gt;
 public FeatureCollection executeQuery(String query, Collection exceptions, TaskMonitor monitor)&lt;br /&gt;
&lt;br /&gt;
Here is the piece of code which will return a Connection able to parse the XYZ file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import java.io.File;&lt;br /&gt;
import java.util.ArrayList;&lt;br /&gt;
import java.util.Collection;&lt;br /&gt;
import java.io.BufferedReader;&lt;br /&gt;
import java.io.FileReader;&lt;br /&gt;
import com.vividsolutions.jts.geom.Coordinate;&lt;br /&gt;
import com.vividsolutions.jts.geom.GeometryFactory;&lt;br /&gt;
import com.vividsolutions.jump.feature.*;&lt;br /&gt;
import com.vividsolutions.jump.io.datasource.*;&lt;br /&gt;
import com.vividsolutions.jump.task.TaskMonitor;&lt;br /&gt;
&lt;br /&gt;
import org.apache.log4j.Logger;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * XYZ Data Source&lt;br /&gt;
 * @author Michael MICHAUD&lt;br /&gt;
 * @version 0.1 (2007-04-29)&lt;br /&gt;
 */&lt;br /&gt;
public class XYZDataSource extends DataSource {&lt;br /&gt;
    &lt;br /&gt;
    private static final Logger LOG = Logger.getLogger(XYZDataSource.class);&lt;br /&gt;
    &lt;br /&gt;
    /**&lt;br /&gt;
     * Creates a new Connection to this DataSource.&lt;br /&gt;
     */&lt;br /&gt;
    public Connection getConnection() {&lt;br /&gt;
        &lt;br /&gt;
        try {&lt;br /&gt;
            &lt;br /&gt;
            return new Connection() {&lt;br /&gt;
                &lt;br /&gt;
                public FeatureCollection executeQuery(String query, Collection exceptions, TaskMonitor monitor) {&lt;br /&gt;
                    BufferedReader br = null;&lt;br /&gt;
                    try {&lt;br /&gt;
                        File file = new File(getProperties().get(FILE_KEY).toString());&lt;br /&gt;
                        int index = new Integer(getProperties().get(InstallXYZDataSourceQueryChooserPlugIn.INDEX).toString()).intValue();&lt;br /&gt;
                        br = new BufferedReader(new FileReader(file));&lt;br /&gt;
                        String line;&lt;br /&gt;
                        FeatureSchema fs = new FeatureSchema();&lt;br /&gt;
                        fs.addAttribute(&amp;quot;GEOMETRY&amp;quot;, AttributeType.GEOMETRY);&lt;br /&gt;
                        for (int i = 0 ; i &amp;lt; index ; i++) {&lt;br /&gt;
                            fs.addAttribute(&amp;quot;Attribute_&amp;quot;+i, AttributeType.STRING);&lt;br /&gt;
                        }&lt;br /&gt;
                        GeometryFactory gf = new GeometryFactory();&lt;br /&gt;
                        FeatureCollection coll = new FeatureDataset(fs);&lt;br /&gt;
                        while(null != (line = br.readLine())) {&lt;br /&gt;
                            if (line.trim().length()==0) continue;&lt;br /&gt;
                            String[] ss = line.split(&amp;quot;(\\s|,|;|\\|)&amp;quot;);&lt;br /&gt;
                            try {&lt;br /&gt;
                                double x = Double.parseDouble(ss[index-1]);&lt;br /&gt;
                                double y = Double.parseDouble(ss[index]);&lt;br /&gt;
                                double z = ss.length &amp;gt; (index+1) ? Double.parseDouble(ss[index+1]) : Double.NaN;&lt;br /&gt;
                                BasicFeature bf = new BasicFeature(fs);&lt;br /&gt;
                                bf.setGeometry(gf.createPoint(new Coordinate(x,y,z)));&lt;br /&gt;
                                for (int i = 0 ; i &amp;lt; index ; i++) {&lt;br /&gt;
                                    bf.setAttribute(&amp;quot;Attribute_&amp;quot;+i, ss[i]);&lt;br /&gt;
                                }&lt;br /&gt;
                                coll.add(bf);&lt;br /&gt;
                            }&lt;br /&gt;
                            catch(Exception e) {&lt;br /&gt;
                                LOG.debug(&amp;quot;Error reading XYZ file: &amp;quot; + line, e);&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                        br.close();&lt;br /&gt;
                        return coll;&lt;br /&gt;
                    } catch (Exception e) {&lt;br /&gt;
                        LOG.warn(&amp;quot;Error executing query \&amp;quot;&amp;quot; + query + &amp;quot;\&amp;quot;&amp;quot;, e);&lt;br /&gt;
                        exceptions.add(e);&lt;br /&gt;
                        return null;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                public void executeUpdate(String update,&lt;br /&gt;
                                          FeatureCollection featureCollection,&lt;br /&gt;
                                          TaskMonitor monitor) throws Exception {&lt;br /&gt;
                    throw new Exception(&amp;quot;Update is not authorized for this DataSource&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                public void close() {}&lt;br /&gt;
                &lt;br /&gt;
                public FeatureCollection executeQuery(String query, TaskMonitor monitor) throws Exception {&lt;br /&gt;
                    ArrayList exceptions = new ArrayList();&lt;br /&gt;
                    FeatureCollection featureCollection = executeQuery(query, exceptions, monitor);&lt;br /&gt;
                    if (!exceptions.isEmpty()) {&lt;br /&gt;
                        throw (Exception) exceptions.iterator().next();&lt;br /&gt;
                    }&lt;br /&gt;
                    return featureCollection;&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
        }&lt;br /&gt;
        catch(Exception e) {&lt;br /&gt;
            LOG.warn(&amp;quot;Error trying to connect to a GeoConcept Data Source&amp;quot;, e);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    public boolean isWritable() {&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to add a &amp;#039;save as&amp;#039; driver, you&amp;#039;ll have to implement the _executeUpdate_ method of the Connection &lt;br /&gt;
&lt;br /&gt;
For more complex drivers, it may be useful to create special classes as JUMPReaders and/or JUMPWriters.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3 - The Driver Extension&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The following class, derived from Extension, will transform the PlugIn into an extension, which is like an external PlugIn :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import com.vividsolutions.jump.workbench.plugin.Extension;&lt;br /&gt;
import com.vividsolutions.jump.workbench.plugin.PlugInContext;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Configuration file for xyz driver extension.&lt;br /&gt;
 * @version 0.1 (2007-04-29)&lt;br /&gt;
 */&lt;br /&gt;
public class XYZDriverConfiguration extends Extension {&lt;br /&gt;
    public void configure(PlugInContext context) throws Exception {&lt;br /&gt;
        new InstallXYZDataSourceQueryChooserPlugIn().initialize(context);&lt;br /&gt;
    }&lt;br /&gt;
    public String getName() {return XYZdriver(read-only)&amp;quot;;}&lt;br /&gt;
    public String getVersion() {return &amp;quot;0.1 (2007-04-29)&amp;quot;;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hope that example will help you to write your own driver. I wrote this small tutorial because it gave me headache to understand the api and write a driver for the GeoConcept format.&lt;br /&gt;
If you find errors or want to improve this tutorial, please, feel free to do it.&lt;/div&gt;</summary>
		<author><name>Mentaer</name></author>
	</entry>
</feed>