Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [udig-devel] CRS Chooser

Attached are the new files to use the custom WKT editor as well as to
have the current map CRS selected when the preferences are opened.


On Sat, 05 Mar 2005 11:26:25 -0800, Jesse Eichar
<jeichar@xxxxxxxxxxxxxxx> wrote:

> > Populating the list in createComponent makes the PreferencePage
> > resize.  If I add a PaintListener and create the list in paintControl,
> > I get a flicker while it repaints.  I could just fix the size of the
> > list, but I was hoping to let it size with the Preference Page.  Any
> > suggestions?
> 
> I fixed this problem for you.  In the CRSPreferencepage the
> doComputeSize() method had to be changed to return the desired
> PreferencePage size.
> 

It still resizes for me.  I updated from the head, but I'll check to
see if I messed something up when I started changing CRSPropertyPage

> Not all CRS objects will have the authority but if it does then you (I
> don't know this code really well) should be able to call crs.getName
> ().getCode(). and if it has a authority this *should* return the code.
> 

That wasn't the place for it, but after hunting around for a while, I
think I got it to work.  The chooser now selects the current map CRS
when it is created.

> > Should I strip the "AUTHORITY" portions out of the WKT editor?  I'm
> > not sure how that will affect the rest of the system either way.  If
> > the user is copying the WKT from somewhere else, it should probably
> > keep the authority information.  But if they're just mucking about
> > with the WKT I'm not sure what will happen if the same authority code
> > is in the system with different properties.
> >
> I talked to Rueben and he says to leave it, it is part of the WKT
> representation.
> 
Rueben followed up with an email that seemed to indicate I should
remove them.  They're removed for a CRS from an authority factory but
if the user adds them in, I blindly pass them on to the WKT parser.

> One suggestion I might make is to consider using jface Viewers
> (ListViewer in this case) instead of actual lists.  These tend to be a
> bit easier to use that Lists.  It is not required at all but worth
> looking into.  I've attached code where I converted your List in your
> CRSChooser to a ListViewer.
> 
Is it worth the effort to move my filter logic to a ViewerFilter and
implement my own ContentProvider and LabelProvider?  I think the only
real use in doing so would be to use objects besides the string
labels.  I don't think I can pull out the list of CRS object's from an
authority factory, just the codes.  So I assume that leaving the
implementation the way it is  will be fine for now.

Mike
/*
 * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
 * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 */
package net.refractions.udig.project.internal.ui;

import java.util.Iterator;

import net.refractions.udig.project.Map;
import net.refractions.udig.project.provider.CRSChooser;
import net.refractions.udig.project.render.ViewportModel;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.internal.dialogs.PropertyDialog;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * A user interface that allow a user to modify/create and set the current and default
 * CoordinateReferenceSystems used for maps.
 * <p>
 * Responsibilities:
 * <ul>
 * <li>Allow creation of user defined CRSs</li>
 * <li>Allow modification of the current CRS</li>
 * <li>Allow the CRS of a map to be set</li>
 * <li>Allow the default CRS of a workbench to be set</li>
 * </ul>
 * </p>
 * 
 * @author jeichar
 * @since 0.3
 */
public class CRSPropertyPage extends PropertyPage implements IWorkbenchPreferencePage {

    private IWorkbench workbench = null;
    
    CRSChooser chooser=new CRSChooser();

    /**
     * Construct <code>CRSPropertyPage</code>.
     */
    public CRSPropertyPage() {
        setTitle("Coordinate Systems");
    }

    /**
     * @see org.eclipse.jface.preference.IPreferencePage#performOk()
     */
    public boolean performOk() {
        CoordinateReferenceSystem crs=chooser.getCRS();
        if( crs==null )
            return false;
        
        PropertyDialog dialog = (PropertyDialog) getContainer();

        Iterator iter = ((StructuredSelection) dialog.getSelection()).iterator();
        while( iter.hasNext() ) {
            Object item = iter.next();
            if (item instanceof Map) {
                Map map = (Map) item;
                map.getViewportModel().setCRS(crs);
            }
        }

        return super.performOk();
    }

    /**
     * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
     */
    protected void performDefaults() {
        PropertyDialog dialog = (PropertyDialog) getContainer();

        Iterator iter = ((StructuredSelection) dialog.getSelection()).iterator();
        while( iter.hasNext() ) {
            Object item = iter.next();
            if (item instanceof Map) {
                Map map = (Map) item;
                map.getViewportModel().setCRS(ViewportModel.DEFAULT_CRS);
            }
        }
    }

    /**
     * @see org.eclipse.jface.preference.PreferencePage#getPreferenceStore()
     */
    public IPreferenceStore getPreferenceStore() {
        if (workbench != null) {
            return workbench.getPreferenceStore();
        }

        return ProjectUIPlugin.getDefault().getPreferenceStore();
    }

    /**
     * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
     * @param parent
     */
    protected Control createContents( Composite parent ) {
        PropertyDialog dialog = (PropertyDialog) getContainer();

        Iterator iter = ((StructuredSelection) dialog.getSelection()).iterator();
        while( iter.hasNext() ) {
            Object item = iter.next();
            if (item instanceof Map) {
                Map map = (Map) item;
                Control control=chooser.createControl(parent,map.getViewportModel().getCRS());
                return control;
            }
        }
        return null;
    }

    /**
	 * @see org.eclipse.jface.preference.PreferencePage#doComputeSize()
	 */
	protected Point doComputeSize() {
		return new Point(500,500);
	}
    
    /**
     * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
     */
    public void init( IWorkbench workbench ) {
        this.workbench = workbench;
    }

}

/*
 * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
 * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 */
package net.refractions.udig.project.provider;

import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.geotools.referencing.FactoryFinder;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * Creates a Control for choosing a Coordinate Reference System.
 * 
 * @author jeichar
 * @since 0.6.0
 */
public class CRSChooser {

    ListViewer codesList;
    Text searchText;
    Text wktText;
    CoordinateReferenceSystem selectedCRS;
    Matcher matcher;
    boolean painted;
    boolean customWkt;

    public CRSChooser() {
        matcher = Pattern.compile(".*?\\(([^(]*)\\)$").matcher("");
    }

    private Control createCustomCRSControl( Composite parent ) {
        Composite composite = new Composite(parent, SWT.NONE);

        GridLayout layout = new GridLayout();
        composite.setLayout(layout);

        GridData gridData = new GridData();
        Label editorLabel = new Label(composite, SWT.NONE);
        editorLabel.setText("Coordinate Reference System WKT:");
        editorLabel.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        wktText = new Text(composite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
        wktText.addModifyListener(new ModifyListener(){
            public void modifyText( ModifyEvent e ) {
                customWkt = true;
            }
        });
        fillWktText();
        wktText.setLayoutData(gridData);
        return composite;
    }

    private Control createStandardCRSControl( Composite parent ) {
        Composite composite = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        composite.setLayout(layout);

        GridData gridData = new GridData();
        Label codesLabel = new Label(composite, SWT.NONE);
        codesLabel.setText("Coordinate Reference Systems:");
        codesLabel.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        searchText = new Text(composite, SWT.SINGLE | SWT.BORDER);
        searchText.setLayoutData(gridData);
        searchText.addModifyListener(new ModifyListener(){
            public void modifyText( ModifyEvent e ) {
                fillCodesList();
            }
        });

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        codesList = new ListViewer(composite);
        codesList.setContentProvider(new ArrayContentProvider());
        codesList.setLabelProvider(new LabelProvider());
        codesList.addSelectionChangedListener(new ISelectionChangedListener(){

            public void selectionChanged( SelectionChangedEvent event ) {
                selectedCRS = null;
                String crsCode = (String) ((IStructuredSelection) codesList.getSelection())
                        .getFirstElement();
                if (crsCode != null) {
                    matcher.reset(crsCode);
                    if (matcher.matches()) {
                        selectedCRS = createCRS(matcher.group(1));
                        fillWktText();
                        customWkt = false;
                    }
                }
            }

        });

        codesList.getControl().setLayoutData(gridData);
        /*
         * fillCodesList() by itself resizes the Preferences Page but in the paintlistener it
         * flickers the window
         */
        fillCodesList();

        return composite;
    }

    /**
     * Creates the CRS PreferencePage root control with a CRS already selected
     * 
     * @param parent PreferencePage for this chooser
     * @param crs current CRS for the associated map
     * @return control for the PreferencePage
     */
    public Control createControl( Composite parent, CoordinateReferenceSystem crs ) {
        selectedCRS = crs;
        Control control = createControl(parent);
        if (selectedCRS != null) {
            fillWktText();
            if(selectedCRS.getIdentifiers().length>0){
                codesList.setSelection(new StructuredSelection(selectedCRS.getName().getCode() + " ("
                        + selectedCRS.getIdentifiers()[0].toString() + ")"), true);
                customWkt=false;
            }
        }
        return control;
    }

    /**
     * Creates the CRS PreferencePage root control with no CRS selected
     * 
     * @param parent PreferencePage for this chooser
     * @return control for the PreferencePage
     */
    public Control createControl( Composite parent ) {
        GridData gridData = null;

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        TabFolder folder = new TabFolder(parent, SWT.NONE);
        folder.setLayoutData(gridData);

        TabItem standard = new TabItem(folder, SWT.NONE);
        standard.setText("Standard CRS");
        Control stdCRS = createStandardCRSControl(folder);
        standard.setControl(stdCRS);

        TabItem custom = new TabItem(folder, SWT.NONE);
        custom.setText("Custom CRS");
        Control cstCRS = createCustomCRSControl(folder);
        custom.setControl(cstCRS);

        return folder;
    }

    /**
     * checks if all keywords in filter array are in input
     * 
     * @param input test string
     * @param filter array of keywords
     * @return true, if all keywords in filter are in the input, false otherwise
     */
    protected boolean matchesFilter( String input, String[] filter ) {
        for( String match : filter ) {
            if (!input.contains(match))
                return false;
        }
        return true;
    }

    /**
     * filters all CRS Names from all available CRS authorities
     * 
     * @param filter array of keywords
     * @return Set of CRS Names which contain all the filter keywords
     */
    protected Set<String> filterCRSNames( String[] filter ) {
        Set<String> descriptions = new TreeSet<String>();
        for( Object object : FactoryFinder.getCRSAuthorityFactories() ) {
            CRSAuthorityFactory factory = (CRSAuthorityFactory) object;

            try {
                Set<String> codes = factory.getAuthorityCodes(CoordinateReferenceSystem.class);
                for( Object codeObj : codes ) {
                    String code = (String) codeObj;
                    String description;
                    try {
                        description = factory.getDescriptionText(code).toString();
                    } catch (Exception e1) {
                        description = "UNNAMED";
                    }
                    description += " (" + code + ")";
                    if (matchesFilter(description.toUpperCase(), filter))
                        descriptions.add(description);
                }
            } catch (FactoryException e) {
                // list = null;
            }
        }
        return descriptions;
    }

    /**
     * populates the codes list with a filtered list of CRS names
     */
    protected void fillCodesList() {
        String[] searchParms = searchText.getText().toUpperCase().split(" ");
        Set<String> descriptions = filterCRSNames(searchParms);
        String list[] = descriptions.toArray(new String[descriptions.size()]);
        codesList.setInput(list);
    }
    
    /**
     * 
     * populates the wkt editor with the wkt of the current crs
     *
     */
    protected void fillWktText(){
        if (selectedCRS != null){
            String text = selectedCRS.toWKT();
            wktText.setText(text.replaceAll(",\\s*AUTHORITY\\[.*?\\]",""));
        }
    }

    /**
     * creates a CRS from a code when the appropriate CRSAuthorityFactory is unknown
     * 
     * @param code CRS code
     * @return CRS object from appropriate authority, or null if the appropriate factory cannot be
     *         determined
     */
    protected CoordinateReferenceSystem createCRS( String code ) {
        if (code == null)
            return null;
        for( Object object : FactoryFinder.getCRSAuthorityFactories() ) {
            CRSAuthorityFactory factory = (CRSAuthorityFactory) object;
            try {
                return (CoordinateReferenceSystem) factory.createObject(code);
            } catch (FactoryException e2) {
                // then we have the wrong factory
                // is there a better way to do this?
            }
        }
        return null; // should throw an exception?
    }

    /**
     * returns the selected CRS
     * 
     * @return selected CRS
     */
    public CoordinateReferenceSystem getCRS() {
        if(customWkt){
            try {
                return FactoryFinder.getCRSFactory().createFromWKT(wktText.getText());
            } catch (NoSuchElementException e) {
                // TODO Catch e
            } catch (FactoryException e) {
                // TODO Catch e
            }
        }
        return selectedCRS;
    }

}

Back to the top