/*******************************************************************************
* Copyright (c) 2008, 2012 Stepan Rutz.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stepan Rutz - initial implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.geomap.internal.geomapbrowser;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.nebula.widgets.geomap.GeoMap;
import org.eclipse.nebula.widgets.geomap.GeoMapUtil;
import org.eclipse.nebula.widgets.geomap.OsmTileServer;
import org.eclipse.nebula.widgets.geomap.PointD;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Text;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* A {link {@link Page} that allows searching use the OSM Nominatim tool.
* The OSM namefinder has its own usage policies. Please check them out before
* you use it.
*
* @author stepan.rutz@gmx.de
* @version $Revision$
*/
public class SearchPage extends AbstractPage implements Page {
private static final Logger log = Logger.getLogger(SearchPage.class.getName());
public static final class SearchResult {
private String type;
private double lat, lon;
private String name;
private String category;
private String info;
public SearchResult() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
public double getLon() {
return lon;
}
public void setLon(double lon) {
this.lon = lon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String toString() {
return "SearchResult [category=" + category + ", info=" + info + ", lat=" + lat + ", lon=" + lon
+ ", name=" + name + ", type=" + type + "]";
}
}
private final GeoMapBrowser mapBrowser;
private ProgressBar progressBar;
private GridData progressBarLayoutData;
private Text searchText;
private boolean searching;
private ArrayList<SearchResult> results = new ArrayList<SearchResult>();
private Link searchLink;
public SearchPage(GeoMapBrowser mapBrowser) {
this.mapBrowser = mapBrowser;
}
protected void widgetDisposed(DisposeEvent e) {
// nothing to dispose
}
protected void initContent(final PageContainer container, Composite composite) {
addHeaderRow(container, composite, "Infopanel");
addInfoText(container, composite, "Toggling the tile-server changes the way " +
"the map is rendered. Click on the mapinfos link to see details " +
"about the currently rendered part of the map.");
addActionLink(container, composite, "<a>Toggle Rendering</a>", new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
GeoMap geoMap = mapBrowser.getGeoMap();
int next = (Arrays.asList(OsmTileServer.TILESERVERS).indexOf(geoMap.getTileServer()) + 1) % OsmTileServer.TILESERVERS.length;
geoMap.setTileServer(OsmTileServer.TILESERVERS[next]);
}
});
addActionLink(container, composite, "Show <a>Technical Mapinfos</a>", new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
mapBrowser.getPageContainer().showPage(mapBrowser.getPageContainer().indexOfPage(mapBrowser.getInfoPage()));
}
});
addActionLink(container, composite, "Show <a>Europe</a>", new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
GeoMap geoMap = mapBrowser.getGeoMap();
geoMap.setZoom(5);
Point position = GeoMapUtil.computePosition(new PointD(5.5, 52.2), geoMap.getZoom());
geoMap.setCenterPosition(position);
geoMap.redraw();
}
});
addHeaderRow(container, composite, "Search location");
addInfoText(container, composite, "Enter any location or landmark site to search openstreetmap's " +
"genuine namefinder database. Hit return to perform the search.");
{
Label label = new Label(composite, SWT.NONE);
label.setText("Search:");
label.setLayoutData(new GridData());
container.adapt(label);
}
{
Composite wrap = new Composite(composite, SWT.NONE);
wrap.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
container.adapt(wrap);
GridLayout layout = new GridLayout(2, false);
//layout.marginLeft = 8;
//layout.marginHeight = 0;
wrap.setLayout(layout);
searchText = new Text(wrap, SWT.BORDER | SWT.SEARCH);
searchText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
searchText.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
startSearch();
}
public void widgetDefaultSelected(SelectionEvent e) {
startSearch();
}
});
searchLink = new Link(wrap, SWT.NONE);
searchLink.setText("<a>Go</a>");
container.adapt(searchLink);
searchLink.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
startSearch();
}
});
}
progressBar = new ProgressBar(composite, SWT.SMOOTH | SWT.INDETERMINATE);
container.adapt(progressBar);
progressBarLayoutData = new GridData(GridData.FILL, GridData.BEGINNING, true, false, 2 , 1);
progressBarLayoutData.exclude = true;
progressBar.setVisible(false);
progressBar.setLayoutData(progressBarLayoutData);
addHeaderRow(container, composite, "Actions");
addInfoText(container, composite, GeoMap.ABOUT_MSG);
}
void startSearch() {
if (searching)
return;
final String search = searchText.getText();
if (search == null || search.length() == 0)
return;
searching = true;
setupSearchGui();
Thread t = new Thread(new Runnable() {
public void run() {
doSearchInternal(search);
}
});
t.setName("Background Seacher \"" + search + "\"");
t.start();
}
private void setupSearchGui() {
progressBarLayoutData.exclude = false;
progressBar.setVisible(true);
searchText.setEnabled(false);
searchLink.setEnabled(false);
progressBar.getParent().layout();
}
private void tearDownSearchGui() {
progressBarLayoutData.exclude = true;
progressBar.setVisible(false);
searchText.setEnabled(true);
searchLink.setEnabled(true);
progressBar.getParent().layout();
}
private static final String NAMEFINDER_URL_FORMAT = "http://nominatim.openstreetmap.org/search?format=xml&q={0}"; //$NON-NLS-1$
private void doSearchInternal(final String newSearch) {
results.clear();
try {
String args = URLEncoder.encode(newSearch.trim(), "UTF-8"); //$NON-NLS-1$
String path = MessageFormat.format(NAMEFINDER_URL_FORMAT, args);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.newSAXParser().parse(path, new DefaultHandler() {
private StringBuilder chars;
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("place".equals(qName)) { //$NON-NLS-1$
SearchResult result = new SearchResult();
result.setType(attributes.getValue("type")); //$NON-NLS-1$
result.setLat(tryDouble(attributes.getValue("lat"))); //$NON-NLS-1$
result.setLon(tryDouble(attributes.getValue("lon"))); //$NON-NLS-1$
result.setName(attributes.getValue("display_name")); //$NON-NLS-1$
results.add(result);
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
// ignore
}
public void characters(char[] ch, int start, int length) throws SAXException {
if (chars != null)
chars.append(ch, start, length);
}
private double tryDouble(String s) {
try {
return Double.valueOf(s);
} catch (Exception e) {
return 0d;
}
}
});
} catch (Exception e) {
log.log(Level.SEVERE, "failed to search for \"" + newSearch + "\"", e);
}
getComposite().getDisplay().asyncExec(new Runnable() {
public void run() {
try {
ResultsPage resultsPage = mapBrowser.getResultsPage();
resultsPage.setSearch(newSearch);
mapBrowser.getPageContainer().showPage(mapBrowser.getPageContainer().indexOfPage(resultsPage));
resultsPage.setResults(results.toArray(new SearchResult[results.size()]));
} finally {
searching = false;
tearDownSearchGui();
}
}
});
}
}