Index: /applications/editors/josm/wmsadapter/orthofotos-bern/README.txt
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/README.txt	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/README.txt	(revision 16723)
@@ -0,0 +1,85 @@
+----------------------------------------------------------------------------
+                     WMS Adapter for Orthofotos of Bern
+----------------------------------------------------------------------------
+
+
+This adapter translates JOSMs tile requests for orthofotos of Bern to tile
+requests which can be handled by the WMS server of  Bern.
+
+The adapter is a small web application which runs on your local computer.  
+
+
+   +--------+                   +-------------+              +-----------+
+   |  JOSM  |   tile request    | WMS Adapter |  tile req    | City Map  |                  
+   |        | --------------->  |    for      | -----------> | of Bern   |
+   |        |        tile       | Orthofotos  |     tile     |           |
+   |        |  <-------------   |   of Bern   | <----------  |           |
+   +--------+                   +-------------+              +-----------+
+
+The adapter is responsible
+o  for maintaining a valid session with the WMS server of Bern
+o  translating lat/lon-coordinates in WGS84 to x/y-coordinates of CH1903
+
+Limitations:
+o although I tried to mimic the behaviour of a standard browser like Firefox as
+  closely as possible I wasn't able to automatically retrieve a valid session
+  ID from the WMS server of Bern. Session IDs retrieved automatically 
+  timed out immediately.
+  
+  You therefore have to configure the WMS adapter with a valid session ID which
+  you have to retrieve from the WMS server of Bern using your preferred web
+  browser (see below in section Usage). 
+
+o currently, tiles for orthofotos of Bern overlap considerably. 
+  
+
+INSTALLATION
+------------
+
+o  Download the latest orthofoto-bern-wms-adapter-<version>.zip from
+   http://www.guggis.ch/orthofoto-bern-wms-adapter/ 
+
+o  Unzip orthofoto-bern-wms-adapter-<version>.zip
+
+   
+
+CONFIGURING JOSM 
+----------------
+o Add the WMS adapter as WMS server 
+  - press F12 to launch the configuration dialog
+  - select the configuration screen for WMS server
+  - add an entry with
+       menu name = Orthofotos Bern 
+       WMS-URL   = http://localhost:8787/orthofotos-bern?action=getmap
+       
+    
+
+USAGE 
+------------
+o Start the WMS adapter 
+
+   c:\> java -jar winstone-0.9.10.jar --warfile=orthofoto-bern-wms-adapter.war 
+
+   Use --httpPort=<port> to set another port the adapter is listening too.
+ 
+o Get a valid session ID
+
+   - Launch your browser and point it at
+     http://www.stadtplan.bern.ch/TBInternet/default.aspx?User=1
+    
+   - View the current set of cookies in your browser and copy the cookie
+        for domain  stadtplan.bern.ch
+        with name   ASP.NET_SessionId 
+     to the clipboard
+     
+o Configure WMS adapter with the session ID
+   - Point your browser at 
+        http://localhost:8787/orthofotos-bern
+   - Enter the Session ID retrieved in the previous step
+     and click on "Submit"
+     
+o Use JOSM
+  - You may now use JOSM to retrieve orthofotos of Bern
+     
+   
+    
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/VERSION.txt
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/VERSION.txt	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/VERSION.txt	(revision 16723)
@@ -0,0 +1,4 @@
+
+Built-On: 2009-07-29 19:04:09
+Build-ID: 15			
+		
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/build.number
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/build.number	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/build.number	(revision 16723)
@@ -0,0 +1,4 @@
+#Build Number for ANT. Do not edit!
+#Wed Jul 29 19:04:08 CEST 2009
+build.number=16
+1=
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/BoundingBox.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/BoundingBox.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/BoundingBox.java	(revision 16723)
@@ -0,0 +1,114 @@
+package ch.guggis.josm.bern.servlet;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+public class BoundingBox {
+
+	public static final NumberFormat COORD_FORMAT = new DecimalFormat(
+			"###0.0000000", new DecimalFormatSymbols(Locale.US));
+
+	static public BoundingBox convertWGS84toCH1903(BoundingBox bbox) {
+		BoundingBox ret = new BoundingBox();
+		WGS84Coordinates wgs84Coordinates = new WGS84Coordinates(
+				bbox.getLeft(), bbox.getBottom());
+		CH1903Coordinates c1 = wgs84Coordinates.convertToCHCoordinates();
+		wgs84Coordinates = new WGS84Coordinates(bbox.getRight(), bbox.getTop());
+		CH1903Coordinates c2 = wgs84Coordinates.convertToCHCoordinates();
+
+		ret.setLeft(c1.getY());
+		ret.setBottom(c1.getX());
+		ret.setRight(c2.getY());
+		ret.setTop(c2.getX());
+		return ret;
+	}
+
+	private double left;
+	private double bottom;
+	private double right;
+	private double top;
+
+	public BoundingBox() {
+	}
+
+	public BoundingBox(double left, double bottom, double right, double top) {
+		this.left = left;
+		this.bottom = bottom;
+		this.right = right;
+		this.top = top;
+	}
+
+	public void fromString(String bbox) throws IllegalArgumentException,
+			NumberFormatException {
+		String[] segments = bbox.split(",");
+		if (segments == null || segments.length != 4) {
+			throw new IllegalArgumentException(
+					"unexpected format of 'bbox'. got '" + bbox + "'");
+		}
+		for (int i = 0; i < 4; i++) {
+			try {
+				double d = Double.parseDouble(segments[i]);
+				switch (i) {
+				case 0:
+					left = d;
+					break;
+				case 1:
+					bottom = d;
+					break;
+				case 2:
+					right = d;
+					break;
+				case 3:
+					top = d;
+					break;
+				default: /* should not happen */
+				}
+			} catch (NumberFormatException e) {
+				throw e;
+			}
+		}
+	}
+
+	@Override
+	public String toString() {
+		String bbox = String.format("BBOX=%s,%s,%s,%s", COORD_FORMAT
+				.format(left), COORD_FORMAT.format(bottom), COORD_FORMAT
+				.format(right), COORD_FORMAT.format(top));
+		return bbox;
+	}
+
+	public double getLeft() {
+		return left;
+	}
+
+	public void setLeft(double left) {
+		this.left = left;
+	}
+
+	public double getBottom() {
+		return bottom;
+	}
+
+	public void setBottom(double bottom) {
+		this.bottom = bottom;
+	}
+
+	public double getRight() {
+		return right;
+	}
+
+	public void setRight(double right) {
+		this.right = right;
+	}
+
+	public double getTop() {
+		return top;
+	}
+
+	public void setTop(double top) {
+		this.top = top;
+	}
+
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/CH1903Coordinates.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/CH1903Coordinates.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/CH1903Coordinates.java	(revision 16723)
@@ -0,0 +1,39 @@
+package ch.guggis.josm.bern.servlet;
+
+/**
+ * represents a pair of CH1903 coordinates
+ * 
+ * @see <a
+ *      href="http://de.wikipedia.org/wiki/Schweizer_Koordinatensystem">Schweizer
+ *      Koordinatensystem(Wikipedia)</a>
+ */
+public class CH1903Coordinates {
+
+	private double x;
+	private double y;
+
+	public CH1903Coordinates() {
+		this(0, 0);
+	}
+
+	public CH1903Coordinates(double x, double y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	public double getX() {
+		return x;
+	}
+
+	public void setX(double x) {
+		this.x = x;
+	}
+
+	public double getY() {
+		return y;
+	}
+
+	public void setY(double y) {
+		this.y = y;
+	}
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/OrthofotoBernWMSAdapter.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/OrthofotoBernWMSAdapter.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/OrthofotoBernWMSAdapter.java	(revision 16723)
@@ -0,0 +1,513 @@
+package ch.guggis.josm.bern.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import ch.guggis.josm.bern.servlet.exception.IllegalParameterValueException;
+import ch.guggis.josm.bern.servlet.exception.MissingParameterException;
+import ch.guggis.josm.bern.servlet.exception.OrthofotoBernWMSAdapterException;
+
+/**
+ * This servlet adapts the Web Map Service (WMS) of the city of Bern for the
+ * JOSM WMS plugin.
+ * 
+ * It converts lat/lon coordinates (EPSG:4326) used in JOSM to x/y-coordinates
+ * in the Swiss Grid (CH1903).
+ * 
+ * The servlet includes methods for retrieving two cookies from the WMS server
+ * of the city of Bern:
+ * <ul>
+ * <li>{@see #COOKIE_NAME_SESSION_ID1} is the cookie generated by the main map
+ * site www.stadthplan.bern.ch</li>
+ * <li>{@see #ASPSESSIONIDQSADRCCB} is the cookie generated by the WMS server</li>
+ * </ul>
+ * 
+ * You can still try to retrieve these session IDs invoking the servlet with
+ * 
+ * <pre>
+ *     http://.....?action=show-session-id
+ * </pre>
+ * 
+ * but unfortunately the map server doesn't accept these cookies in subsequent
+ * orthofoto tile requests. You therefore have to get a valid session id with
+ * your favorite browser and enter it in the servlet using the servlets web form
+ * - see README.txt for more information.
+ * 
+ * <strong>Actions</strong>
+ * <dl>
+ * <dt>http://....?action=ping</dt>
+ * <dd>Checks whether the servlet is alive (not the WMS server) and replies an
+ * OK text</dd>
+ * 
+ * <dt>http://....?action=show-session-id</dt>
+ * <dd>Retrieves the session ids from the WMS server and displays them</dd>
+ * 
+ * <dt>http://....?action=set-session-id[&session-id=thesessionid]</dt>
+ * <dd>Displays a form for entering the session id to be used in tile requests.</dd>
+ * 
+ * <dt>http://....?action=get-map&</dt>
+ * <dd>Displays a form for entering the session id to be used in tile requests.
+ * Use <code>width</code>, <code>height</code>, and <code>bbox</code> as
+ * parameters. Replies the orthofoto tile from the web server of the city of
+ * Bern.</dd>
+ * <dl>
+ * 
+ */
+public class OrthofotoBernWMSAdapter extends HttpServlet {
+
+	static public final String USER_AGENT = "Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5";
+
+	/** the name of the session cookie used by www.stadtplan.bern.ch */
+	static public final String COOKIE_NAME_SESSION_ID1 = "ASPSESSIONIDQSADRCCB";
+	/**
+	 * the name of the session cookie used by the WMS server of
+	 * www.stadtplan.bern.ch
+	 */
+	static public final String COOKIE_NAME_SESSION_ID2 = "ASP.NET_SessionId";
+
+	static public final String DEFAULT_URL_GEN_SESSION_ID1 = "http://www.stadtplan.bern.ch/";
+	static public final String DEFAULT_URL_GEN_SESSION_ID2 = "http://www.stadtplan.bern.ch/TBInternet/WebMapPageLV.aspx";
+
+	/**
+	 * the default URL to enter Berns map application. The session ID is
+	 * retrieved from this URL
+	 */
+	static public final String DEFAULT_URL_MAP_APP_ENTRY = "http://www.stadtplan.bern.ch/TBInternet/WebTitlePage.aspx";
+	// static public final String DEFAULT_URL_MAP_APP_ENTRY =
+	// "http://www.stadtplan.bern.ch/TBInternet/WebMapPageLV.aspx";
+
+	/** the default URL to retrieve map tiles from Berns map application */
+	static public final String DEFAULT_URL_MAP_REQUESTS = "http://www.stadtplan.bern.ch/TBInternet/WebMapServer.aspx?VERSION=1.0.0&REQUEST=GETMAP&TYPE=11&LAYERS=TBI_orthofoto_08.mwf&FORMAT=image/jpeg&EXCEPTIONS=image/jpeg";
+
+	/** the logger */
+	private static Logger logger = Logger
+			.getLogger(OrthofotoBernWMSAdapter.class.getName());
+
+	HashMap<String, String> cityMapSessionCookies = new HashMap<String, String>();
+
+	/**
+	 * remembers session cookie retrieved from Berns map server
+	 * 
+	 * @param headerField
+	 *            the header field with the session cookie
+	 * 
+	 */
+	protected void rememberSessionCookie(String headerField) {
+		String cookie = headerField;
+		cookie = cookie.substring(0, cookie.indexOf(";"));
+		String cookieName = cookie.substring(0, cookie.indexOf("="));
+		String cookieValue = cookie.substring(cookie.indexOf("=") + 1, cookie
+				.length());
+		cityMapSessionCookies.put(cookieName, cookieValue);
+		if (!(cookieName.equals(COOKIE_NAME_SESSION_ID1) || cookieName
+				.equals(COOKIE_NAME_SESSION_ID2))) {
+			logger.warning("unexpected name for session cookie. name="
+					+ cookieName + ",value=" + cookieValue);
+		}
+	}
+
+	/**
+	 * retrieves the main session ID from Berns map application
+	 * 
+	 * @exception IOException
+	 *                thrown, if an IO exception occurs
+	 */
+	protected void getCityMapSessionID1() throws IOException {
+		URL url = null;
+
+		try {
+			url = new URL(DEFAULT_URL_GEN_SESSION_ID1);
+		} catch (MalformedURLException e) {
+			// should not happen, but log it anyway
+			logger.log(Level.SEVERE, e.toString());
+			return;
+		}
+
+		cityMapSessionCookies.remove(COOKIE_NAME_SESSION_ID1);
+
+		try {
+			URLConnection con = url.openConnection();
+			con.setRequestProperty("User-Agent", USER_AGENT);
+			con.connect();
+			String headerName = null;
+			for (int i = 1; (headerName = con.getHeaderFieldKey(i)) != null; i++) {
+				logger.log(Level.INFO, headerName + "="
+						+ con.getHeaderField(headerName));
+				if (headerName.equals("Set-Cookie")) {
+					String cookie = con.getHeaderField(i);
+					rememberSessionCookie(cookie);
+				}
+			}
+			if (cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID1) == null) {
+				logger.log(Level.WARNING, "response did not include cookie "
+						+ COOKIE_NAME_SESSION_ID1
+						+ ". Further requests to the WMS server will timeout.");
+			} else {
+				logger.info("successfully retrieved cookie "
+						+ COOKIE_NAME_SESSION_ID1 + ". value is <"
+						+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID1)
+						+ ">");
+			}
+		} catch (IOException e) {
+			logger.log(Level.SEVERE,
+					"failed to retrieve Session from Stadtplan Bern. "
+							+ e.toString());
+			throw e;
+		}
+	}
+
+	/**
+	 * retrieves the session ID from Berns WMS server. It mimics the behaviour
+	 * of a standard browser as closely as possible. The request header fields
+	 * sent to the remote server are those Firefox sends. They include a valid
+	 * session ID from www.stadtplan.bern.ch.
+	 * 
+	 * @exception IOException
+	 *                thrown, if an IO exception occurs
+	 */
+
+	protected void getCityMapSessionID2() throws IOException {
+		URL url = null;
+
+		try {
+			url = new URL(DEFAULT_URL_GEN_SESSION_ID2);
+		} catch (MalformedURLException e) {
+			// should not happen, but log it anyway
+			logger.log(Level.SEVERE, e.toString());
+			return;
+		}
+
+		cityMapSessionCookies.remove(COOKIE_NAME_SESSION_ID2);
+
+		try {
+			URLConnection con = url.openConnection();
+			con.setRequestProperty("Host", "www.stadtplan.bern.ch");
+			con.setRequestProperty("Referer", "http://www.stadtplan.bern.ch/");
+			con.setRequestProperty("Cookie", COOKIE_NAME_SESSION_ID1 + "="
+					+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID1));
+			con.setRequestProperty("User-Agent", USER_AGENT);
+			con
+					.setRequestProperty("Accept",
+							"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+			con.setRequestProperty("Accept-Language",
+					"de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
+			con.setRequestProperty("Accept-Encoding", "gzip,deflate");
+			con.setRequestProperty("Accept-Charset",
+					"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
+
+			con.connect();
+			String headerName = null;
+			for (int i = 1; (headerName = con.getHeaderFieldKey(i)) != null; i++) {
+				logger.log(Level.INFO, headerName + "="
+						+ con.getHeaderField(headerName));
+				if (headerName.equals("Set-Cookie")) {
+					String cookie = con.getHeaderField(i);
+					rememberSessionCookie(cookie);
+				}
+			}
+			if (cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2) == null) {
+				logger.log(Level.WARNING, "response did not include cookie "
+						+ COOKIE_NAME_SESSION_ID2
+						+ ". Further requests to the WMS server will timeout.");
+			} else {
+				logger.info("successfully retrieved cookie "
+						+ COOKIE_NAME_SESSION_ID2 + ". value is <"
+						+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2)
+						+ ">");
+			}
+
+		} catch (IOException e) {
+			logger.log(Level.SEVERE,
+					"failed to retrieve Session from Stadtplan Bern. "
+							+ e.toString());
+			throw e;
+		}
+	}
+
+	/**
+	 * retrieves a session id from the map application provided by the city of
+	 * bern
+	 * 
+	 * @throws IOException
+	 *             thrown, if the connection to the map application fails
+	 */
+	protected void getCityMapSessionIDs() throws IOException {
+		getCityMapSessionID1();
+		getCityMapSessionID2();
+	}
+
+	/**
+	 * true, if both the session IDs are known; false, otherwise
+	 * 
+	 * @return true, if both the session IDs are known; false, otherwise
+	 */
+	protected boolean hasCityMapSessionIDs() {
+		return cityMapSessionCookies != null
+				&& cityMapSessionCookies.containsKey(COOKIE_NAME_SESSION_ID1)
+				&& cityMapSessionCookies.containsKey(COOKIE_NAME_SESSION_ID2);
+	}
+
+	/**
+	 * handles simple ping request
+	 * 
+	 * @param request
+	 *            the http request
+	 * @param response
+	 *            the http response
+	 * @throws IOException
+	 */
+	protected void handlePing(HttpServletRequest request,
+			HttpServletResponse response) throws IOException {
+		response.setStatus(HttpServletResponse.SC_OK);
+		PrintWriter pw = new PrintWriter(response.getWriter());
+		pw.println("OK");
+
+	}
+
+	/**
+	 * handles a request for the current session ID
+	 * 
+	 * @param req
+	 * @param resp
+	 * @throws IOException
+	 */
+	protected void handleShowSessionId(HttpServletRequest req,
+			HttpServletResponse resp) throws IOException {
+		if (!hasCityMapSessionIDs()) {
+			getCityMapSessionIDs();
+		}
+
+		resp.setStatus(HttpServletResponse.SC_OK);
+		resp.setContentType("text/html");
+		PrintWriter pw = new PrintWriter(resp.getWriter());
+
+		if (cityMapSessionCookies.containsKey(COOKIE_NAME_SESSION_ID1)) {
+			pw.println("OK: " + COOKIE_NAME_SESSION_ID1 + "="
+					+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID1));
+		} else {
+			pw.println("FAIL: " + COOKIE_NAME_SESSION_ID1 + " missing.");
+		}
+		pw.println("</br>");
+		if (cityMapSessionCookies.containsKey(COOKIE_NAME_SESSION_ID2)) {
+			pw.println("OK: " + COOKIE_NAME_SESSION_ID2 + "="
+					+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2));
+		} else {
+			pw.println("FAIL: " + COOKIE_NAME_SESSION_ID2 + " missing.");
+		}
+
+	}
+
+	/**
+	 * handles a tile request
+	 * 
+	 * @param req
+	 *            the request
+	 * @param resp
+	 *            the response
+	 * 
+	 * @throws IOException
+	 *             thrown, if an IO exception occurs
+	 * @throws OrthofotoBernWMSAdapterException
+	 *             thrown, if an exception occurs
+	 */
+	protected void handleTileRequest(HttpServletRequest req,
+			HttpServletResponse resp) throws IOException,
+			OrthofotoBernWMSAdapterException {
+		int width;
+		int height;
+
+		// check parameters
+		//
+		if (req.getParameter("width") == null) {
+			throw new MissingParameterException("width");
+		}
+		try {
+			width = Integer.parseInt(req.getParameter("width"));
+		} catch (NumberFormatException e) {
+			throw new IllegalParameterValueException("width",
+					"illegal int value", e);
+		}
+
+		if (req.getParameter("height") == null) {
+			throw new MissingParameterException("height");
+		}
+		try {
+			height = Integer.parseInt(req.getParameter("height"));
+		} catch (NumberFormatException e) {
+			throw new IllegalParameterValueException("height",
+					"illegal int value", e);
+		}
+
+		if (req.getParameter("bbox") == null) {
+			throw new MissingParameterException("bbox");
+		}
+		BoundingBox bbox = new BoundingBox();
+		try {
+			bbox.fromString(req.getParameter("bbox"));
+		} catch (Exception e) {
+			throw new IllegalParameterValueException("bbox",
+					"failed to parse value", e);
+		}
+
+		// translate bounding box
+		//
+		bbox = BoundingBox.convertWGS84toCH1903(bbox);
+
+		if (!cityMapSessionCookies.containsKey(COOKIE_NAME_SESSION_ID2)) {
+			throw new OrthofotoBernWMSAdapterException(
+					"required session IDs missing. Can't proceed with request.");
+		}
+
+		// build request URL
+		//
+		StringBuffer sb = new StringBuffer();
+		sb.append(DEFAULT_URL_MAP_REQUESTS);
+		sb.append("&WIDTH=");
+		sb.append(width);
+		sb.append("&HEIGHT=");
+		sb.append(height);
+		sb.append("&");
+		sb.append(bbox.toString());
+
+		logger.info("requesting tile with URL <" + sb.toString() + ">");
+
+		URL url;
+		try {
+			url = new URL(sb.toString());
+		} catch (MalformedURLException e) {
+			// should not happen, but log it anyway
+			logger.log(Level.SEVERE, e.toString());
+			throw new OrthofotoBernWMSAdapterException("failed to build URL", e);
+		}
+
+		try {
+			HttpURLConnection con = (HttpURLConnection) url.openConnection();
+			String cookie = String.format("%s=%s", COOKIE_NAME_SESSION_ID2,
+					cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2));
+			logger.info("setting cookie <" + cookie + ">");
+			con.setRequestProperty("Cookie", cookie);
+			con.setRequestProperty("User-Agent", USER_AGENT);
+			con.setRequestProperty("Host", "www.stadtplan.bern.ch");
+			con
+					.setRequestProperty("Referer",
+							"http://www.stadtplan.bern.ch/TBInternet/WebMapPageLV.aspx");
+			con.setRequestProperty("Accept",
+					"image/jpeg,image/*;q=0.8,*/*;q=0.5");
+			con.setRequestProperty("Accept-Language",
+					"de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
+			con.setRequestProperty("Accept-Encoding", "gzip,deflate");
+			con.setRequestProperty("Accept-Charset",
+					"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
+			con.connect();
+			resp.setContentType("image/jpeg");
+			InputStream in = con.getInputStream();
+			OutputStream out = resp.getOutputStream();
+			byte[] buf = new byte[1024];
+			int read = in.read(buf);
+			while (read > 0) {
+				out.write(buf, 0, read);
+				read = in.read(buf);
+			}
+			in.close();
+
+		} catch (Exception e) {
+			throw new OrthofotoBernWMSAdapterException(
+					"failed to fetch orthofoto tile from map server", e);
+		}
+	}
+
+	protected void errorIllegalAction(HttpServletRequest request,
+			HttpServletResponse response, String action) throws IOException {
+		response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED,
+				"unexpected value for parameter 'action'. Got " + action);
+	}
+
+	protected void renderSessionIDInputForm(HttpServletRequest req,
+			HttpServletResponse resp) throws IOException {
+		String msg = "<html><head></head><body>\n"
+				+ "<h1>WMS Adapter for Orthofots of Bern</h1>"
+				+ "Please open <a href=\"http://www.stadtplan.bern.ch/TBInternet/default.aspx?User=1\">the city map of Bern</a> in your browser.</br>"
+				+ "Then lookup the cookie <strong>ASP.Net_SessionId</strong> for domain <strong>www.stadtplan.bern.ch</strong> in your browser and enter it in the form below.</br>\n"
+				+ "<form action=\""
+				+ req.getRequestURL()
+				+ "\">\n"
+				+ "<input type=\"hidden\" name=\"action\" value=\"set-session-id\">\n"
+				+ "<input type=\"text\" name=\"session-id\" value=\"\">\n"
+				+ "<input type=\"submit\" value=\"Submit\">\n" + "</form>\n"
+				+ "</body></html>";
+
+		PrintWriter pw = new PrintWriter(resp.getWriter());
+		pw.println(msg);
+	}
+
+	protected void handleSetSessionId(HttpServletRequest req,
+			HttpServletResponse resp) throws OrthofotoBernWMSAdapterException,
+			IOException {
+		if (req.getParameter("session-id") == null) {
+			throw new MissingParameterException("session-id");
+		}
+
+		cityMapSessionCookies.put(COOKIE_NAME_SESSION_ID2, req
+				.getParameter("session-id"));
+		logger.info("set session id <"
+				+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2) + ">");
+
+		String msg = "<html><head></head><body>\n"
+				+ "<h1>WMS Adapter for Orthofots of Bern</h1>"
+				+ "Session ID &lt;"
+				+ cityMapSessionCookies.get(COOKIE_NAME_SESSION_ID2)
+				+ "&gt; successfully set. You may now start to request map tiles from JOSM.</br></br>"
+				+ "<a href=\""
+				+ req.getRequestURL()
+				+ "?action=getmap&bbox=7.4441276,46.9539095,7.4458911,46.9556731&width=500&height=499\">Click here for an example</a>"
+				+ "</body></html>";
+
+		PrintWriter pw = new PrintWriter(resp.getWriter());
+		pw.println(msg);
+
+	}
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+
+		logger.info("handling request <" + req.getRequestURI() + ">");
+
+		String action = req.getParameter("action");
+		if ("ping".equals(action)) {
+			handlePing(req, resp);
+		} else if ("show-session-id".equals(action)) {
+			handleShowSessionId(req, resp);
+		} else if ("getmap".equals(action)) {
+			try {
+				handleTileRequest(req, resp);
+			} catch (Exception e) {
+				logger.log(Level.SEVERE,
+						"exception while handling tile request.", e);
+			}
+		} else if ("set-session-id".equals(action)) {
+			try {
+				handleSetSessionId(req, resp);
+			} catch (OrthofotoBernWMSAdapterException e) {
+				throw new ServletException(
+						"exception caught while handing setting session id", e);
+			}
+		} else {
+			renderSessionIDInputForm(req, resp);
+		}
+	}
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/WGS84Coordinates.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/WGS84Coordinates.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/WGS84Coordinates.java	(revision 16723)
@@ -0,0 +1,71 @@
+package ch.guggis.josm.bern.servlet;
+
+/**
+ * represents geodetic coordinates according to the World Geodetic System 1984.
+ * Latitude and longitude are in decimal degrees.
+ * 
+ */
+
+public class WGS84Coordinates {
+
+	private double lon;
+	private double lat;
+
+	public WGS84Coordinates() {
+	}
+
+	public WGS84Coordinates(double lon, double lat) {
+		this.lon = lon;
+		this.lat = lat;
+	}
+
+	public double getLon() {
+		return lon;
+	}
+
+	public void setLon(double lon) {
+		this.lon = lon;
+	}
+
+	public double getLat() {
+		return lat;
+	}
+
+	public void setLat(double lat) {
+		this.lat = lat;
+	}
+
+	/**
+	 * converts WGS84 coordinates to CH coordinates
+	 * 
+	 * @return CH coordinates
+	 * @see http://de.wikipedia.org/wiki/Schweizer_Koordinatensystem#
+	 *      Umrechnung_WGS84_auf_CH1903
+	 */
+	public CH1903Coordinates convertToCHCoordinates() {
+
+		double phi = 3600d * this.lat;
+		double lambda = 3600d * this.lon;
+
+		double phiprime = (phi - 169028.66d) / 10000d;
+		double lambdaprime = (lambda - 26782.5d) / 10000d;
+
+		// precompute squares for lambdaprime and phiprime
+		//
+		double lambdaprime_2 = Math.pow(lambdaprime, 2);
+		double phiprime_2 = Math.pow(phiprime, 2);
+
+		double north = 200147.07d + 308807.95d * phiprime + 3745.25d
+				* lambdaprime_2 + 76.63d * phiprime_2 - 194.56d * lambdaprime_2
+				* phiprime + 119.79d * Math.pow(phiprime, 3);
+
+		double east = 600072.37d + 211455.93d * lambdaprime - 10938.51d
+				* lambdaprime * phiprime - 0.36d * lambdaprime * phiprime_2
+				- 44.54d * Math.pow(lambdaprime, 3);
+
+		CH1903Coordinates chCoord = new CH1903Coordinates(north, east);
+
+		return chCoord;
+	}
+
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/IllegalParameterValueException.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/IllegalParameterValueException.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/IllegalParameterValueException.java	(revision 16723)
@@ -0,0 +1,36 @@
+package ch.guggis.josm.bern.servlet.exception;
+
+public class IllegalParameterValueException extends
+		OrthofotoBernWMSAdapterException {
+	private String parameterName;
+	private String reason;
+	private Throwable cause;
+
+	public IllegalParameterValueException(String parameterName, String reason) {
+		this.parameterName = parameterName;
+		this.reason = reason;
+	}
+
+	public IllegalParameterValueException(String parameterName, String reason,
+			Throwable cause) {
+		this(parameterName, reason);
+		this.cause = cause;
+	}
+
+	@Override
+	public Throwable getCause() {
+		return cause;
+	}
+
+	@Override
+	public String getMessage() {
+		StringBuilder sb = new StringBuilder();
+		sb.append(String.format("invalid parameter value for parameter '%s'."));
+		if (reason != null) {
+			sb.append("reason:");
+			sb.append(reason);
+		}
+		return sb.toString();
+	}
+
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/MissingParameterException.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/MissingParameterException.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/MissingParameterException.java	(revision 16723)
@@ -0,0 +1,24 @@
+package ch.guggis.josm.bern.servlet.exception;
+
+public class MissingParameterException extends OrthofotoBernWMSAdapterException {
+
+	private String parameterName = null;
+
+	public MissingParameterException(String parameterName) {
+		super();
+		this.parameterName = parameterName;
+	}
+
+	@Override
+	public Throwable getCause() {
+		// TODO Auto-generated method stub
+		return super.getCause();
+	}
+
+	@Override
+	public String getMessage() {
+		return String.format(
+				"mandatory paramter '%s' was missing in the servlet request",
+				parameterName);
+	}
+}
Index: /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/OrthofotoBernWMSAdapterException.java
===================================================================
--- /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/OrthofotoBernWMSAdapterException.java	(revision 16723)
+++ /applications/editors/josm/wmsadapter/orthofotos-bern/src/ch/guggis/josm/bern/servlet/exception/OrthofotoBernWMSAdapterException.java	(revision 16723)
@@ -0,0 +1,25 @@
+package ch.guggis.josm.bern.servlet.exception;
+
+public class OrthofotoBernWMSAdapterException extends Exception {
+
+	public OrthofotoBernWMSAdapterException() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+	public OrthofotoBernWMSAdapterException(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+		// TODO Auto-generated constructor stub
+	}
+
+	public OrthofotoBernWMSAdapterException(String arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	public OrthofotoBernWMSAdapterException(Throwable arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+}
