Thursday, November 12, 2009

Use Mapviewer to cache WMS maps as map tiles

Warning: Not all map services providers(WMS included) allow their maps to be cached by others. Before using MapViewer to cache maps served by any third party map services provider, you should consult the map services provider to make sure you're allowed to cache their maps.

With the use of map source adapter, you can let MapViewer cache maps rendered by an external map services provider as map tiles and use them in your Oracle Maps application.

A map source adapter is basically a Java class that is called by MapViewer to construct requests understood by the external map services provider. MapViewer calls the map source adapter to construct map requests, send them to the map services provider, fetch the maps and cache them as map tiles. Mapviewer is shipped with a map source adapter for WMS, which you can use to cache maps served by a WMS server. To better understand how the map source adapter works, you can take a look at the source code of the WMS adapter, which can be found at $mapviewer_deploy_home/web/WEB-INF/tileserver/mvadapter/mcsadapter/WMSAdapter.java.

Here are the steps to create a map tile layer using the WMS adapter.

1. Login MapViewer's web admin page.

2. Enter the tile layer creation interface. Choose "External" when being asked for the the type of map source.

3. On the tile layer creation page, you need to provide the configuration information for the map tile layer. Besides regular tile layer configuration options such as name, data source, coordinate system, zoom level definition and etc, you also need to provide the following information specific to an external map tile layer.
  • Map service URL: The URL of the external map services provider. In the case of WMS, it should be the URL of the WMS server, e.g. http://www.myhost.com/mywms.
  • Adapter class: The path of the WMS adapter class. It should be mcsadapter.WMSAdapter.
  • Jar file location: the full path of the file mvadapter.jar, which is under $mapviewer_deploy_home/web/WEB-INF/tileserver/mvadapter.
  • Check "Adapter properties".
  • Add WMS parameters(name and value) for your map requests. Example:


name value
----------- -----
version 1.1.1
srs EPSG:4326
layers Countries,Borders
format image/png
transparent true

4. Click "Submit" to create the tile layer.

Thursday, October 22, 2009

Google maps tiles in a MapViewer app

Earlier this year I posted an article on how to display MapViewer generated tiles in a Google Maps application. In this post I will discuss how to display Google Maps tiles in a MapViewer/Oracle Maps application.

Starting with MapViewer 11g Release 1, a built-in Google Maps tile layer class has been added to the MapViewer's JavaScript API. It is called MVGoogleTileLayer. Unfortunately it is not officially documented for 11g Release 1 (but will be in the upcoming patch and future releases).

This API class is a thin wrapper of the official Google Maps API, and as such you will need to get your own Google Maps key for your application.

The basic steps for displaying Google Maps in your Oracle Maps application are:

Step 1. Import the Google Maps JavaScript library

<script src="http://maps.google.com/maps?file=api&v=2&key=[your-Google-Maps-API-key]"
type="text/javascript"></script>

Again you will need to get your own Google Maps key from Google and set it in the above script tag.

Step 2. Add a Google Maps tile layer to the MVMapView object

mapview = new MVMapView(document.getElementById("map"), baseURL);
baseMap = new MVGoogleTileLayer() ;
mapview.addMapTileLayer(baseMap);

That's all it takes to display a basic Google Maps map in your MapViewer application.

You can also change the type of map you get from Google Maps. Here is an example:

var mapType = G_HYBRID_MAP;
basemap.setMapType(mapType) ;

Where basemap is a MVGoogleTileLayer instance. Note that G_HYBRID_MAP is a constant defined by the imported Google Maps API library and represents a hybrid Google Maps type. The full description for this method is:

setMapType(mapType)
This method sets the type of the map, which can be one of the following predefined Google Map types, G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP and G_PHYSICAL_MAP.

The only other method defined on the MVGoogleTileLayer class is getMapType(), which returns the current map type of the Google Maps being displayed.


Finally, as part of your application cleanup, please call Google Maps' clean-up method GUnload() (such as in the HTML page's unload event listener).

Monday, October 5, 2009

displaying table of longitude and latitude points

While in most cases your location data are stored in an Oracle database as SDO_GEOMETRY type, sometimes you may have certain point data stored simply as two numeric columns in a table (as longitude/latitude for instance). So how can you display these points using MapViewer? In this article I will present one of the more elegant options, namely using function-based index.

The overall steps are:
1. Create a database function that returns a SDO_GEOMETRY object from two numeric values.

2. Treat this function as a SDO_GEOMETRY column by creating an entry in the USER_SDO_GEOM_METADATA view.

3. Create a Spatial index on this function

4. Create a pre-defined theme in Map Builder for this function.

5. Profit.

Lets say we have an existing table CITIES in the schema SCOTT.

SQL> desc cities;
Name Null? Type
------------------------------- -------- ----------------------------
ID NUMBER
NAME VARCHAR2(100)
ALIASE VARCHAR2(500)
COUNTR VARCHAR2(100)
POP NUMBER
LON NUMBER
LAT NUMBER


Where the two columns LON and LAT represent each city's location.

So the first step is to create a function that generates an actual SDO_GEOMETRY object when supplied with a pair of (lon, lat).

Step 1. Create function

Execute the following while logged in as SCOTT:

create or replace function get_geometry(lon in number,
lat in number)
return SDO_GEOMETRY deterministic is
begin
return sdo_geometry(2001, 8307, sdo_point_type(lon, lat, NULL),NULL, NULL);
end;
/


Note that the function must be deterministic.

Step 2. Populate Spatial metadata view

Now that we have a function, we need to make Spatial and MapViewer treat it as if it's a real SDO_GEOMETRY column. All we need to do is insert an entry for this function in the USER_SDO_GEOM_METADATA view. Again execute the following as SCOTT:

insert into user_sdo_geom_metadata values('CITIES',
'scott.get_geometry(lon,lat)',
sdo_dim_array(
sdo_dim_element('Longitude', -180, 180, 0.005),
sdo_dim_element('Latitude', -90, 90, 0.005)),
8307);

commit;


Step 3. Create a Spatial index:
We now need to create a Spatial index on the function, just like with any SDO_GEOMETRY columns. Without a Spatial index, your SQL spatial queries won't work, and MapViewer cannot display them easily.

create index CITIES_SDX on
CITIES(get_geometry(lon,lat))
indextype is mdsys.spatial_index;


Step 4. Create a Map Builder theme

Open Map Builder, click Show Data to open the data navigator. You should find the CITIES table under the user SCOTT. Right click it and choose "Create Geometry Theme". You should see that the function we just created is already picked up as the Spatial column, as illustrated in this screen shot:



Go through the normal process and complete the theme creation. You now have a pre-defined MapViewer theme that can be used to display those (X,Y) data. You can also add this theme to your AJAX map as a FoI (Feature Of Interest) layer.

Friday, September 18, 2009

Why am I getting "Request string is too long for Oracle Maps' non-AJAX remoting"?

You may get this error when your application is running in a web domain that is different from the one where mapviewer is running, e.g. your application is running on hostA and your MapViewer is running on hostB. Please note that different http ports on the same host are also two different domains.

Oracle Maps client relies on AJAX to exchange data with the MapViewer server. In the scenario described above, when the client needs to send an AJAX request to the Mapviewer server, it is sending the request across domain, which is prohibited by all major web browsers. This is known as the cross-domain constraint.

We implemented our own non-AJAX remoting mechanism to overcome this so that the Oracle Maps client can talk to the MapViewer in a different domain. This workaround has one limitation - the length of the non-AJAX remoting request can not exceed certain limit. If the requests are within the limit, everything will work great. That's why in most cases Oracle Maps application works fine across domain out of box.

But things are not always that simple. If your application displays some complex theme based FOI layers, especially those with JDBC queries and/or dynamic styles, the underlying FOI requests might become too long to be handled by the non-AJAX remoting mechanism. As the result, you will get the error message mentioned in the title.

To solve this problem, you have two options.

Option 1
---------
Deploy a MapViewer to the same domain where your application is running. You don't have to configure this MapViewer to do any real mapping. You'll only need its proxy servlet, which can forward all requests from the client to the remote MapViewer that does the real mapping. The client will send all AJAX requests to the proxy servlet, which will then forward the requests to the remote MapViewer. Because the servlet is in the same domain as the application, there will be no cross-domain constraint when the client talks to the proxy.

In your application code, you need to call MVMapView.enableXMLHTTP(true) at the beginning of your showMap function.

function showMap()
{
MVMapView.enableXMLHTTP(true);
var baseURL = "http://hostB/mapviewer";
mapview = new MVMapView(mapDiv, baseURL) ;
...
}

Option 2
---------
Configure a reverse proxy in your HTTP server to forward requests sent to it to the remote MapViewer. This reverse proxy should accept requests sent to http://hostA/mapviewer/* and forward them to http://hostB/mapviewer/*. If your HTTP server is Apache, then this can be done by adding the following two lines inside mod_proxy section in httpd.conf.

<IfModule mod_proxy.c>
ProxyRequests Off

<Directory proxy:*>
Order deny,allow
Deny from 127.0.0.1
Allow from all
</Directory>

ProxyPass /mapviewer/ http://hostB/mapviewer/
ProxyPassReverse /mapviewer/ http://hostB/mapviewer/

</IfModule>


Your application code should act as if the MapViewer is in the local domain, because the proxy is acting as a local MapViewer instance. For better performance, you should explicitly specify the remote tile server URL when creating a tile layer. By doing so, you're telling the Oracle Maps client to get the map tiles directly from the remote MapViewer, not via the proxy, which also means better performance.

function showMap()
{
var baseURL = "http://"+document.location.host+"/mapviewer" ;
mapview = new MVMapView(mapDiv, baseURL) ;
tileLayer = new MVMapTileLayer("myds.my_map", "http://hostB/mapviewer/mcserver");
...
}


Friday, July 31, 2009

display labels with superscripts and subscripts

My colleague Albert Godfrind just sent me a nice tip on how to display map labels containing subscripts and superscripts. Thought I will share it with you all.

I found the following to work for me. I create a table (using a NVARCHAR2 column for storing the label), then I populate it with two rows: one using the standard label, one using the sub/superscript notation.

create table labels (
id number,
label nvarchar2(30),
geom sdo_geometry
);

insert into labels (id, label, geom)
values (
1,
'm3 CH4 / min',
sdo_geometry (2001, null, sdo_point_type (1,1,null), null, null)
);
insert into labels (id, label, geom)
values (
2,
unistr('m\00B3 CH\2084 / min'),
sdo_geometry (2001, null, sdo_point_type (1,2,null), null, null)
);
commit;

insert into user_sdo_geom_metadata values (
'LABELS',
'GEOM',
sdo_dim_array (
sdo_dim_element ('x', 0, 100, 0.05),
sdo_dim_element ('y', 0, 100, 0.05)
),
null
);
commit;
create index labels_sx on labels (geom) indextype is mdsys.spatial_index;

Mapviewer then nicely renders the super/sub scripts as illustrated!

Tuesday, July 28, 2009

Java API: setting array-type binding variables for a theme

MapViewer supports pre-defined themes that contain query conditions with one or more binding variables. For instance, we can define a theme COUNTIES_BY_STATES that displays all the counties whose State abbreviations are in a list supplied by user at run time.

The query condition for this theme may look like the following:

( state_abrv in (select column_value from table(:1)) )


In other words, when this theme is to be displayed, it will require a list of State abbreviations such as 'CA', 'WA', 'NY' as the value for its query's binding variable. Note that this binding variable itself requires a SQL TABLE type.

So how do you supply such an array of state abbreviation codes to the MapViewer from your Java application? The following shows the sample code that does just that.

String [] values = { "MA","NY","ME","NH" }; //list of state abbreviations
ArrayParameter states = new ArrayParameter("MV_STRINGLIST",values);
Object [] values = new Object[1]; //array containing all binding variable values
values[0] = states;
mapViewer.setThemeBindingParameters("COUNTIES_BY_STATES", values);

Note that basically you must use MapViewer's public ArrayParameter class to supply the list of states (or list of any strings/numbers) for a single table-type binding variable. Because in this case there is only one binding variable in this theme's query, the values array contains only one element, the ArrayParameter instance.

Note also MV_STRINGLIST is a SQL list/table type created by MapViewer automatically in each data source (database schema). Please check the 11g MapViewer user's guide on more information about this pre-created type.

multi-line labels

MapViewer supports labels or annotations that contain a newline char (ASCII char code 10). However by default such characters are ignored by MapViewer during the map text labeling process, and the label text appears in a single line (unless there is a text wrap width specified.)

To force MapViewer to honor the newline chars in a theme's label texts, the theme must use a TEXT labeling style with the "Honor Newline" attribute set to true. This can be done from MapBuilder's TEXT style editor, by simply selecting the Honor newline check box. Now any theme that uses this TEXT style will automatically display any label text containing newline characters with multiple lines.

If your application is written using the MapViewer Java API, and you need to add individual geo-features with multi-line text labels to your map, then additional caution must be taken when constructing the label text with the newline char. Namely, the label text for a feature must be constructed like this:

String textLabel = "LINE 1" + "%26#10;" + "LINE 2";

Typically in an XML document (the Java API eventually will convert your current map request into an XML request before sending it to the MapViewer server), one uses &#10; to represent the newline char(10). Because we are sending the entire map request string using HTTP POST from map client to the server, the character '&' itself must be escaped as '%26', hence the odd-looking string %26#10; representing an HTTP-safe newline character.

So this is how you can construct a new feature with this text as its label:

mapViewer.addPointFeature(centreX, centreY, 8265, "C.RED", textLabel, "MY_TEXT_STYLE",
null, null, true);

where MY_TEXT_STYLE should be a TEXT type style with "Honor newline" enabled.

Monday, July 6, 2009

MapViewer 11g R1 production kit released

We are excited to announce that as part of Oracle's Fusion Middleware 11g R1 release, MapViewer 11g R1 is now available for download! Please navigate to OTN MapViewer home page here: http://www.oracle.com/technology/products/mapviewer/index.html. The 11g R1 software can be found under the Software link on the right side of the page.

Note that this release includes all the features and bug fixes of MapViewer patch 10.1.3.3 for WebLogic server, and we strongly encourage users to upgrade to the 11g R1 release to take advantage of the many new enhancements and a more stable Oracle Maps JavaScript API.

It supports not only WebLogic server (and Oracle AS/OC4J 10.1.3.x), but also Tomcat 6.x and JBoss 4.x. The full certification matrix can be found here:
http://www.oracle.com/technology/products/mapviewer/htdocs/j2ee_server_support.html

For the complete list of all the new and enchanced features of MapViewer 11g R1, please check out the New and Changed section of the MapViewer User's Guide, also downloadable from the OTN MapViewer home.

Friday, June 19, 2009

Running MapViewer on a WebLogic server cluster

This post describes how to deploy and run MapViewer version 11g R1 on a basic WebLogic cluster.

For information and instructions on how to setup a simple WebLogic cluster, please check out this excellent blog post. If you followed his instructions, you will have a WebLogic domain that contains a cluster of 3 managed servers. Lets call this the main domain. You will also create a second domain that contains a load balancing servlet (HttpClusterServlet) deployed as its only web application. Lets call this the proxy domain. The main domain's managed servers will be listening on ports 7003,7004 and 7005, while the proxy domain will be listening on 7001. You will deploy MapViewer to the main domain (specifically, to its cluster which is composed of the 3 managed servers), but access MapViewer via port 7001 such as http://localhost:7001/mapviewer. Each web session accessing MapViewer will be redirected to one of the 3 managed server's in a round-robin fashion.

Load balancing servlet's web.xml
While following Andrejus' instructions, when it's about time to deploy the load balancing servlet to the proxy domain, you will need to create a web.xml file for the servlet. You can use the sample web.xml, however for MapViewer to function completely, I have attached my own web.xml below. The main difference is that in my example I added url patterns for .jspx, .png, .gif and .jpeg so these files also get redirected to the managed servers from the proxy servlet.

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd";>

<web-app>

<servlet>
<servlet-name>HttpClusterServlet</servlet-name>
<servlet-class>
weblogic.servlet.proxy.HttpClusterServlet
</servlet-class>

<init-param>
<param-name>WebLogicCluster</param-name>
<param-value>
localhost:7003|localhost:7004|localhost:7005
</param-value>
</init-param>

</servlet>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>/mapviewer</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HttpClusterServlet</servlet-name>
<url-pattern>*.jpeg</url-pattern>
</servlet-mapping>

</web-app>


Deploy MapViewer
Deploying MapViewer to a cluster is actually no different from deploying it to a regular WebLogic server. Basically, follow these simple steps (for more details, please check out the MapViewer User's Guide which has a step-by-step guide):

  1. Create a WebLogic JDBC data source for your spatial database schema. Deploy (install) this data source to the cluster! This container data source will be the basis of the MapViewer data source to be created in step 3.
  2. Unpack mapviewer.ear and setup the required directory tree structure (again details can be found in MapViewer's User's Guide). This will be the working directory tree for MapViewer! Every WLS clustered server will start its own MapViewer instance out of this directory. Each started MapViewer instance will then load the config file from the WEB-INF/conf folder, as well as saving all generated map images and tiles into this same directory tree. If your managed servers span different physical machines, then this folder needs to be on a shared network drive accessible by all the managed servers.
  3. Modify the MapViewer config file, located in the WEB-INF/conf/ folder, to set a proper logging level. Most importantly, you need to create a MapViewer data source based on the container data source you created in step 1. A simple example of such a data source looks like this:
    <map_data_source name="mvdemo"
    container_ds="jdbc/mvdemo"
    number_of_mappers="7"
    allow_jdbc_theme_based_foi="true"
    />
    Another thing you must modify in the config file, is the <save_images_at> element. Make sure the url attribute is set to a URL that points to the proxy domain, for instance "http://localhost:7001/mapviewer/images". And change the path attribute to "../../images".
  4. Go to the console web page of the main domain, and deploy MapViewer from the exploded directory tree. Note that when presented with the "Soure accessibility" page during deployment, make sure you select the last option ("I will make the deployment accessible from the following location")! This is extremely important, as it ensures the MapViewer files are shared by all managed servers, and generated tiles/maps are shared also.
  5. After deployment is done, simply access MapViewer from your browser http://localhost:7001/mapviewer. Everything should work just fine as if you are accessing a normal MapViewer server.
  6. Verify that there are 3 MapViewer instances are running. Go to the exploded MapViewer folder, and change into the directory mapviewer.ear/web.war/WEB-INF/conf/. There you should see a file mv_proc.id. Open it in any text editor, and you should see a number 3. This means MapViewer has acknowleged there are 3 separate instances running.

You now have a MapViewer deployed and running on a cluster of managed WebLogic servers. Each MapViewer instance will be accessing the same database schema using the same cluster-wide container data source for optimal usage of the JDBC connections. MapViewer will intelligently and automatically avoid file name conflicts when generating and saving map images, FOI images as well map tiles. This is critical in a clustered environment since all MapViewer instances share a single physical working directory tree (the exploded MapViewer folder).

Friday, May 29, 2009

Using OpenStreetMap tiles with Oracle FMW MapViewer

The recently uploaded tutorial on using DigitalGlobe's online imagery delivery platform with MapViewer
(http://www.oracle.com/technology/sample_code/products/mapviewer/index.html) discuss the MVCustomMapTileLayer interafce for accessing 3rd party tile providers. This post consists of sample code showing how it can be used with OSM tiles from {tiles|tah}.openstreetmap.org.

The sample html code is listed below.

<html>
<head>
<META http-equiv="Content-Type" content="text/html" charset=UTF-8">
<TITLE>OpenStreetMap as external tile layer</TITLE>
<!--
<link rel="stylesheet" type="text/css" href="/mapviewer/fsmc/tutorial/t.css" />
-->
<link rel="stylesheet" type="text/css" href="http://sdolnx2.us.oracle.com:7777/mapviewer/fsmc/tutorial/t.css" />

<!-- Change to your mapviewer instance e.g. localhost:8888/mapviewer/fsmc/jslib/oraclemaps.js
<script language="Javascript" src="http://localhost:8888/mapviewer/fsmc/jslib/oraclemaps.js"></script>
-->

<script language="Javascript" src="http://sdolnx2.us.oracle.com:7777/mapviewer/fsmc/jslib/oraclemaps.js"></script>

<script language=javascript>

// set up a map tile configuration so MapViewer knows the tiling scheme, i.e.
// the number of zoom levels (20), tile size (256x256)
// the tile width & height in real world coordinates at each zoom level
// the coordinate system (Spherical Mercator EPSG:3785), and
// tile image file format (PNG)
var mapConfig=
{
"mapTileLayer":"OSM_MERCATOR",
"format":"PNG",
"coordSys":
{
"srid":3785,
"type":"PROJECTED",
"distConvFactor":1.0,
"minX":-2.0037508E7,"minY":-2.0037508E7,
"maxX":2.0037508E7,"maxY":2.0037508E7
},
"zoomLevels":
[
{"zoomLevel":0,"name":"","tileWidth":4.00750166855785E7,"tileHeight":4.00750166855785E7,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":1,"name":"","tileWidth":2.0037508E7,"tileHeight":2.0037508E7,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":2,"name":"","tileWidth":1.0018754E7,"tileHeight":1.0018754E7,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":3,"name":"","tileWidth":5009377.0,"tileHeight":5009377.0,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":4,"name":"","tileWidth":2504688.5,"tileHeight":2504688.5,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":5,"name":"","tileWidth":1252344.25,"tileHeight":1252344.25,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":6,"name":"","tileWidth":626172.125,"tileHeight":626172.125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":7,"name":"","tileWidth":313086.0625,"tileHeight":313086.0625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":8,"name":"","tileWidth":156543.03125,"tileHeight":156543.03125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":9,"name":"","tileWidth":78271.515625,"tileHeight":78271.515625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":10,"name":"","tileWidth":39135.7578125,"tileHeight":39135.7578125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":11,"name":"","tileWidth":19567.87890625,"tileHeight":19567.87890625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":12,"name":"","tileWidth":9783.939453125,"tileHeight":9783.939453125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":13,"name":"","tileWidth":4891.9697265625,"tileHeight":4891.9697265625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":14,"name":"","tileWidth":2445.98486328125,"tileHeight":2445.98486328125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":15,"name":"","tileWidth":1222.992431640625,"tileHeight":1222.992431640625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":16,"name":"","tileWidth":611.4962158203125,"tileHeight":611.4962158203125,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":17,"name":"","tileWidth":305.74810791015625,"tileHeight":305.74810791015625,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":18,"name":"","tileWidth":152.87405395507812,"tileHeight":152.87405395507812,"tileImageWidth":256,"tileImageHeight":256},
{"zoomLevel":19,"name":"","tileWidth":76.43702697753906,"tileHeight":76.43702697753906,"tileImageWidth":256,"tileImageHeight":256}
]
};

// create a custom map tile layer based on the above tile configuration
// the second argument is a function that implements the getTileURL
// interface for this particular map tile service
var osmBasemap = new MVCustomMapTileLayer(mapConfig, getOSMMapTileURL);

<!-- Change to your mapviewer instance e.g. localhost:8888/mapviewer/fsmc/jslib/oraclemaps.js
var baseURL = "http://localhost:8888/mapviewer" ;

-->
var baseURL = "http://sdolnx2.us.oracle.com:7777/mapviewer" ;
var mapview;
var oraTheme = null ;
var oraTileLayer = null;

function showMap()
{
mapview = new MVMapView(document.getElementById("map"), baseURL);
mapview.addMapTileLayer(osmBasemap);
// near london -36793, 6679715 in SRID 3785
var mpoint = MVSdoGeometry.createPoint(-36793.89, 6679715.42, 3785); // near london 0.1,51.5,8307

mapview.setCenter(mpoint);
mapview.setZoomLevel(10);

mapview.addNavigationPanel() ;
mapview.display();
}

function showMapTileLayer(layerName)
{
mapview.removeMapTileLayer(osmBasemap) ;
eval("mapview.addMapTileLayer("+layerName+"Basemap)") ;
}



function getOSMMapTileURL(tx, ty, tw, th, level)
{
var x = (tx-mapConfig.coordSys.minX)/mapConfig.zoomLevels[level].tileWidth ;
// Mapviewer's tile The tile numbering scheme, or mesh codes, for Oracle Maps’
// (see Section 8.2 of the MapViewer User Guide for more details) tiles is similar
// to that for Google Maps. There only difference is that the Y origin is
// at the lower left rather than the upper left, as is the case for Google tiles
var y = (mapConfig.coordSys.maxY-ty)/mapConfig.zoomLevels[level].tileHeight-1 ;
// use mapnik rendered tiles
//return "http://tile.openstreetmap.org/"+(level)+"/"+x+"/"+y+".png";
// use osmarender instead
return "http://tah.openstreetmap.org/Tiles/tile/"+(level)+"/"+x+"/"+y+".png";
}

</script>
</head>

<body onload="javascript:showMap()">
<h3>Oracle Maps example - Custom map tile layer</h3>
This sample shows how the Oracle Maps GetMapTileURL interface could be used to display OSM map tiles.
It implements code samples and technical details described on OSM sample code pages.
This code is purely a sample explaining how to use the GetMapTileURL interface.<br>
<div id="map" style="width:800px;height:600px"></div>
</body>
</html>

Friday, May 1, 2009

Storing your redline geometries in a database

MapViewer's JavaScript API provides a simple Red-line tool that you can use to let user draw simple shapes on the map. The tool can also return the coordinates of the shape, but there is no built-in mechanism to persistently store these shapes.

In this article I will show you how to easily store such hand drawings in a database table, then display them on the map at a later time just as easily.

The mains steps are:

1. Create a database table with a SDO_GEOMETRY column (to store the geometries). Also create spatial index on it so we can query and display the table contents on our map later on.

2. Create a very simple JSP page that will handle the task of storing a single geometry. It will take an geometry in a simple string format, then store it in the table using JDBC.

3. Create a simple HTML-based Oracle Maps demo page, where user can create a shape on the map using a Redline tool. After getting hold of the Redline geometry, the page will issue an AJAX request to the JSP page, passing various aspects of the geometry (such as its type, SRID, and coordinates) as URL parameters.

4. Display the table as a dynamic theme in the same HTML page.

For more details, please download the zip file that includes the following 3 files:

readme.txt: Instructions for running this demo.
store.jsp: The JSP page that handles geometry storage.
store.html: The main demo page that lets you draw things and display stored geometries.

The complete instructions are in the readme.txt file.

If you have the built-in Oracle Maps tutorials running, this demo should be very easy to set up and run. To run the dmeo, simply open the store.html page in your browser. Post your comments below if you run into any issues. The following is a screenshot of the demo:




Finally, note that you can easily expand the demo to store more fields besides the geometry itself. It is also trivial to display these fields as attributes in the info-tip window when user clicks on any geometry displayed on the map.

Thursday, March 26, 2009

Rotating map symbols using oriented points

Oracle Spatial supports the notion of oriented points, which are sdo_geometry points that contain additional coordinates of the end point for the orientation vector from the point itself, with values between -1 and 1. For instance, you can create an oriented point using the following SQL statement:

INSERT INTO cola_markets VALUES(
91,
'oriented_point',
SDO_GEOMETRY(
2001,
NULL,
NULL,
SDO_ELEM_INFO_ARRAY(1,1,1, 3,1,0),
SDO_ORDINATE_ARRAY(12,14, 0.3,0.2))
);

The 12,14 identifies the physical coordinates of the point; and the 0.3,0.2 identifies the x and y coordinates (assuming 12,14 as the origin) of the end point of the orientation vector. The resulting orientation vector slopes upward at about a 34-degree angle, as illustrated in this figure:



(The above description and figure are copied from the Oracle Spatial User's Guide).

MapViewer has built-in support for such oriented points. What this means, is that if you apply a MARKER or TEXT style to such points (via a theme defined on the points table for instance), the MARKER (map symbol) or text itself will be oriented on the map according to the angle of its underlying data point, for every displayed data point.

To better illustrate this support, I have created a simple Oracle Maps demo that displays the included oriented_points table in your MVDEMO sample data set, using a pin symbol.


<html>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<META NAME="GENERATOR" CONTENT="JDBC Demo">
<link rel="stylesheet" type="text/css" href="../t.css" />

<script language="Javascript" src="/mapviewer/fsmc/jslib/oraclemaps.js"></script>
<SCRIPT TYPE="text/javascript">
var mapview;
var orientedtheme;
var baseQuery;
var baseURL = "http://"+document.location.host+"/mapviewer";
var mapCenterLon = -122.49;
var mapCenterLat = 37.5106;
var mapZoom = 3;
var mpoint = MVSdoGeometry.createPoint(mapCenterLon,mapCenterLat,8307);
function on_load_mapview()
{
mapview = new MVMapView(document.getElementById("map"), baseURL);
var basemap = new MVMapTileLayer("mvdemo.demo_map");
mapview.addMapTileLayer(basemap);
mapview.setCenter(mpoint);
mapview.setZoomLevel(mapZoom);

orientedtheme= createOrientedTheme();

mapview.addThemeBasedFOI(orientedtheme);

var nav = new MVNavigationPanel() ;
var navPan = new MVMapDecoration(nav,0,0,null,null,4,4) ;
mapview.addMapDecoration(navPan) ;

mapview.display();
}

var pinMarker = "M.CYAN PIN" ;

function createOrientedTheme()
{
baseQuery= "select shape from oriented_points";
var theme = "<themes><theme name='a_dynamic_theme' >" +
"<jdbc_query asis='true' spatial_column='shape' jdbc_srid='8307' " +
"render_style='"+pinMarker+"' datasource='mvdemo'>" + baseQuery +
"</jdbc_query></theme></themes>" ;
var theme = new MVThemeBasedFOI('oriented-theme',theme);
theme.setBringToTopOnMouseOver(true);
return theme;
}

</SCRIPT>
</HEAD>

<BODY onload= javascript:on_load_mapview()>
<h3>Displaying oriented points</h3>
<div id="map" style=" width: 90%; height: 100%;">

</BODY>
</html>



If you save the above code as an HTML file in your MapViewer web directory, then open it from a browser, you will see four pin symbols, rotated according to the angles of the four oriented points from the table.


Monday, March 16, 2009

Creating a separate Admin account for MapViewer (part 2: WLS)

In an earlier blog post I described how to create a separate admin user for the MapViewer admin account in an Oracle Application Server 10.1.3.* installation. This post follows up with instructions for WebLogic Server (version 9 and later).

To set up an admin user account for MapViewer in WLS, you first create a new user in WebLogic, then map this user to the MapViewer admin role in MapViewer's deployment descriptor file (weblogic.xml).

So first lets create a new WLS user. This is done using the WebLogic Admin Console.

Creating a user:

1. In the left pane select Security Realms.
2. On the Summary of Security Realms page select the name of the realm (for example, myrealm) that your MapViewer-deployed domain uses.
3. On the Settings for Realm Name page select Users and Groups > Users.

The User table displays the names of all users defined in the Authentication provider.
4. Click New.
5. In the Name field of the Create New User page enter the name of the user.
User names are case sensitive and must be unique.
6. (Optional) In the Description field, enter a description. The description might be the user's full name.
7. In the Provider drop-down list, select the Authentication provider for the user.

If multiple WebLogic Authentication providers are configured in the security realm, they will appear in the list. Select which WebLogic Authentication provider’s database should store information for the new user.
8. In the Password field, enter a password for the user.

The minimum password length for a user defined in the WebLogic Authentication provider is 8 characters. Do not use the username/password combination weblogic/weblogic in production.
9. Re-enter the password for the user in the Confirm Password field.
10. Click OK to save your changes.

The user name appears in the User table

Assigning the new user to the MapViewer built-in admin role:

1. go to the exploded Mapviwer directory, find weblogic.xml under $MAPVIEWR_HOME/mapviewer.ear/web.war/WEB-INF/
2. open weblogic.xml in a text editor. You will see something like following:

<?xml version = '1.0' encoding = 'US-ASCII'?>

<weblogic-web-app xmlns="http://www.bea.com/ns/weblogic/90">


<security-role-assignment>

<role-name>map_admin_role</role-name>

<principal-name>weblogic</principal-name>

</security-role-assignment>


<security-role-assignment>

<role-name>secure_maps_role</role-name>

<principal-name>weblogic</principal-name>

</security-role-assignment>


</weblogic-web-app>

Replace the strings "weblogic" with the new user name. In my case I used "maplogic" as the new user name:


<?xml version = '1.0' encoding = 'US-ASCII'?>


<weblogic-web-app xmlns="http://www.bea.com/ns/weblogic/90">


<security-role-assignment>

<role-name>map_admin_role</role-name>

<principal-name>maplogic</principal-name>

</security-role-assignment>


<security-role-assignment>

<role-name>secure_maps_role</role-name>

<principal-name>maplogic</principal-name>

</security-role-assignment>


</weblogic-web-app>


Save this file. Then restart the domain. Now you will be able to use the new user and password to log into MapViewer's admin page.

Monday, March 9, 2009

Displaying MapViewer tile layers over Google Maps

A lot of folks have asked how to display a MapViewer tile layer in a Google Maps application. My colleague Jayant Sharma recently wrote a very nice note on this subject, using Google Maps' custom tile layer support. The following are the detailed instructions form his note. They can also be used with other map services such as Microsoft Virtual Earth.

Overview


There are 3 high-level steps involved:
  1. Preparing your Oracle Spatial data by creating the EPSG:3785 coordinate system and transforming the data.
  2. Creating a MapViewer tile layer for your data.
  3. Displaying the MapViewer tile layer over Google Maps using its custom tile layer mechanism.



Adding the EPSG:3785 coordinate system definition

The Google Maps are projected and rendered in a coordinate system known as Spherical Mercator (EPSG id 3785). So the data stored in an Oracle database and rendered by MapViewer into map tiles must also be in that coordinate system. This section describes the steps to add this new EPSG coordinate system definition into an Oracle database.

Note: This requires a 10.2.0.4 database. Earlier versions do not have the required patch that enables transformations (SDO_CS.TRANSFORM) between this (3785) and other (e.g. WGS84 or BNG) coordinate systems.


Note 2: You may want to verify that the SRID 3785 does NOT already exist in the database. To verify, you can run the SQL query "select srid from mdsys.cs_srs where srid=3785" from any database user. If a row is returned, then you already have this SRID, and you should SKIP this section. With the latest 11g Oracle database this SRID does indeed exist out of the box.


Connect to your 10.2.0.4 database instance (with Spatial or Locator installed and enabled) as a privileged user (e.g. a user with the DBA role) and issue the following SQL statements.


insert into MDSYS.SDO_ELLIPSOIDS (

ELLIPSOID_ID,

ELLIPSOID_NAME,

SEMI_MAJOR_AXIS,

UOM_ID,

INV_FLATTENING,

SEMI_MINOR_AXIS,

INFORMATION_SOURCE,

DATA_SOURCE,

IS_LEGACY,

LEGACY_CODE)

VALUES (

7059,

'Popular Visualisation Sphere',

6378137,

9001,

1.0000E+12,

NULL,

null,

'EPSG',

'FALSE',

null);


insert into MDSYS.SDO_DATUMS (

DATUM_ID,

DATUM_NAME,

DATUM_TYPE,

ELLIPSOID_ID,

PRIME_MERIDIAN_ID,

INFORMATION_SOURCE,

DATA_SOURCE,

SHIFT_X,

SHIFT_Y,

SHIFT_Z,

ROTATE_X,

ROTATE_Y,

ROTATE_Z,

SCALE_ADJUST,

IS_LEGACY,

LEGACY_CODE)

VALUES (

6055,

'Popular Visualisation Datum',

'GEODETIC',

7059,

8901,

null,

'EPSG',

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

'FALSE',

NULL);


insert into MDSYS.SDO_COORD_REF_SYSTEM (

SRID,

COORD_REF_SYS_NAME,

COORD_REF_SYS_KIND,

COORD_SYS_ID,

DATUM_ID,

geog_crs_datum_id,

SOURCE_GEOG_SRID,

PROJECTION_CONV_ID,

CMPD_HORIZ_SRID,

CMPD_VERT_SRID,

INFORMATION_SOURCE,

DATA_SOURCE,

IS_LEGACY,

LEGACY_CODE,

LEGACY_WKTEXT,

LEGACY_CS_BOUNDS,

is_valid,

supports_sdo_geometry)

VALUES (

4055,

'Popular Visualisation CRS',

'GEOGRAPHIC2D',

6422,

6055,

6055,

NULL,

NULL,

NULL,

NULL,

null,

'EPSG',

'FALSE',

NULL,

NULL,

NULL,

'TRUE',

'TRUE');


INSERT INTO sdo_coord_ops (

coord_op_id,

coord_op_name,

coord_op_type,

source_srid,

target_srid,

coord_tfm_version,

coord_op_variant,

coord_op_method_id,

UOM_ID_SOURCE_OFFSETS,

UOM_ID_TARGET_OFFSETS,

information_source,

data_source,

show_operation,

is_legacy,

legacy_code,

reverse_op,

is_implemented_forward,

is_implemented_reverse)

VALUES (

19847,

'Popular Visualisation Mercator',

'CONVERSION',

null,

null,

'',

null,

9804,

null,

null,

null,

null,

1,

'FALSE',

null,

1,

1,

1);


insert into MDSYS.SDO_COORD_OP_PARAM_VALS (

COORD_OP_ID,

COORD_OP_METHOD_ID,

PARAMETER_ID,

PARAMETER_VALUE,

PARAM_VALUE_FILE_REF,

UOM_ID)

VALUES (

19847,

9804,

8801, -- Latitude of natural origin

0,

NULL,

9102);


insert into MDSYS.SDO_COORD_OP_PARAM_VALS (

COORD_OP_ID,

COORD_OP_METHOD_ID,

PARAMETER_ID,

PARAMETER_VALUE,

PARAM_VALUE_FILE_REF,

UOM_ID)

VALUES (

19847,

9804,

8802, -- longitude of natural origin

0,

NULL,

9102);


insert into MDSYS.SDO_COORD_OP_PARAM_VALS (

COORD_OP_ID,

COORD_OP_METHOD_ID,

PARAMETER_ID,

PARAMETER_VALUE,

PARAM_VALUE_FILE_REF,

UOM_ID)

VALUES (

19847,

9804,

8805, -- scale factor at natural origin

1,

NULL,

9201);


insert into MDSYS.SDO_COORD_OP_PARAM_VALS (

COORD_OP_ID,

COORD_OP_METHOD_ID,

PARAMETER_ID,

PARAMETER_VALUE,

PARAM_VALUE_FILE_REF,

UOM_ID)

VALUES (

19847,

9804,

8806, -- false easting

0,

NULL,

9001);


insert into MDSYS.SDO_COORD_OP_PARAM_VALS (

COORD_OP_ID,

COORD_OP_METHOD_ID,

PARAMETER_ID,

PARAMETER_VALUE,

PARAM_VALUE_FILE_REF,

UOM_ID)

VALUES (

19847,

9804,

8807, -- false northing

0,

NULL,

9001);


INSERT INTO sdo_coord_ref_system (

srid,

coord_ref_sys_name,

coord_ref_sys_kind,

coord_sys_id,

datum_id,

geog_crs_datum_id,

source_geog_srid,

projection_conv_id,

cmpd_horiz_srid,

cmpd_vert_srid,

information_source,

data_source,

is_legacy,

legacy_code,

legacy_wktext,

legacy_cs_bounds,

is_valid,

supports_sdo_geometry)

VALUES (

3785,

'Popular Visualisation CRS / Mercator',

'PROJECTED',

4499,

null,

6055,

4055,

19847,

Null,

Null,

null,

null,

'FALSE',

null,

null,

null,

'TRUE',

'TRUE');


-- create the tfm_plans, i.e. transformation rule.

-- Note: This will result in an incorrect conversion since it ignores a datum shift

-- between the ellipsoid and the sphere. However the data will match up better

-- on google maps

-- first for wgs84 (8307)


call sdo_cs.create_pref_concatenated_op( 83073785, 'CONCATENATED OPERATION 8307 3785', TFM_PLAN(SDO_TFM_CHAIN(8307, 1000000000, 4055, 19847, 3785)), NULL);


-- 4326 is the EPSG equivalent of 8307

call sdo_cs.create_pref_concatenated_op( 43263785, 'CONCATENATED_OPERATION_4326_3785', TFM_PLAN(SDO_TFM_CHAIN(4326, 1000000000, 4055, 19847, 3785)), NULL);


-- similarly for os bng (oracle srid 81989 or epsg 27700 it is

call sdo_cs.create_pref_concatenated_op( 819893785, 'CONCATENATED OPERATION 81989 3785', TFM_PLAN(SDO_TFM_CHAIN(81989, -19916, 2000021, 1000000000, 4055, 19847, 3785)), NULL);


-- 27700 is the EPSG equivalent of 81989

call sdo_cs.create_pref_concatenated_op( 277003785, 'CONCATENATED_OPERATION_27700_3785', TFM_PLAN(SDO_TFM_CHAIN(27700, -19916, 4277, 1000000000, 4055, 19847, 3785)), NULL);

commit;


Next transform existing data, or a copy of it, into 3785 and use it for generating the tiles. Otherwise the data will be transformed on the fly and that’ll slow down tile generation. The next section will use the MVDEMO sample data set to illustrate the steps.




Setting up a Oracle map tile layer for Google Maps


The MVDEMO sample dataset has tables named counties, cities, interstates, and states and the data is in WGS84 (srid = 8307). There’s also a set of styles, themes, basemaps, and map tile caches defined. These definitions are stored in the USER_SDO_STYLES, SUER_SDO_THEMES, USER_SDO_MAPS, and USER_SDO_CACHED_MAPS views.


The first step is to create a set of tables with the data transformed to 3785.


create table states_3785 as select * from states;

create table interstates_3785 as select * from interstates;

create table cities_3785 as select * from cities;

create table counties_3785 as select * from counties;


update states_3785 set geom = sdo_cs.transform(geom, 3785);

update interstates_3785 set geom = sdo_cs.transform(geom, 3785);

update cities_3785 set location = sdo_cs.transform(location, 3785);

update counties_3785 set geom = sdo_cs.transform(geom, 3785);

commit;


-- insert metadata

insert into user_sdo_geom_metadata values('STATES_3785', 'GEOM',

sdo_dim_array(sdo_dim_element('X', -20037508.3427, 20037508.3427, 0.05), sdo_dim_element('Y', -20037508.3427, 20037508.3427, 0.05)), 3785);


insert into user_sdo_geom_metadata values('INTERSTATES_3785', 'GEOM',

sdo_dim_array(sdo_dim_element('X', -20037508.3427, 20037508.3427, 0.05), sdo_dim_element('Y', -20037508.3427, 20037508.3427, 0.05)), 3785);


insert into user_sdo_geom_metadata values('COUNTIES_3785', 'GEOM',

sdo_dim_array(sdo_dim_element('X', -20037508.3427, 20037508.3427, 0.05), sdo_dim_element('Y', -20037508.3427, 20037508.3427, 0.05)), 3785);


insert into user_sdo_geom_metadata values('CITIES_3785', 'LOCATION',

sdo_dim_array(sdo_dim_element('X', -20037508.3427, 20037508.3427, 0.05), sdo_dim_element('Y', -20037508.3427, 20037508.3427, 0.05)), 3785);


-- create spatial indexes

create index states_3785_sidx on states_3785(geom) indextype is mdsys.spatial_index;

create index interstates_3785_sidx on interstates_3785(geom)

indextype is mdsys.spatial_index;

create index counties_3785_sidx on counties_3785(geom)

indextype is mdsys.spatial_index;

create index cities_3785_sidx on cities_3785(location)

indextype is mdsys.spatial_index parameters(‘layer_gtype=point’);


The second step is to create the themes for these tables and after that a basemap made of these newly created themes. This can be done with MapBuilder or using SQL statements to copy the theme definitions while changing the referenced table names by appending a _3785 to the theme and table name.


insert into user_sdo_themes select name || ‘_3785’, description,

base_table || ‘_3785’, geometry_column, styling_rules

from user_sdo_themes where base_table in (‘STATES’, ‘INTERSTATES’,

‘COUNTIES’, ‘CITIES’) and length(name) < 27


commit;


-- if a theme name is > 27 chars long modify it individually

-- It’ll has to be manually modified in the basemap definition

-- too. E.g there may be one name new_theme_demo_highways_line

-- that can be named theme_demo_highways_line_3785 instead


Next define a basemap using these themes. You can use the following SQL statements to manually create a new basemap named DEMO_MAP_3785 that is similar to DEMO_MAP, then update its referenced theme names and map scales:


insert into user_sdo_maps select ‘DEMO_MAP_3785’, description,definition

from user_sdo_maps where name=’DEMO_MAP’;

update user_sdo_maps set definition=
'<?xml version="1.0" standalone="yes"?>
<map_definition>
<theme name="THEME_DEMO_STATES_3785" />
<theme name="THEME_DEMO_COUNTIES_3785" min_scale="3000000.0" max_scale="0.0" scale_mode="RATIO"/>
<theme name="THEME_DEMO_STATES_LINE_3785" min_scale="3000000.0" max_scale="0.0" scale_mode="RATIO"/>
<theme name="THEME_DEMO_HIGHWAYS_3785"/>
<theme name="THEME_DEMO_CITIES_3785" min_scale="6000000.0" max_scale="0.0" scale_mode="RATIO"/>
<theme name="THEME_DEMO_BIGCITIES_3785"/>
</map_definition>'
where name='DEMO_MAP_3785';
commit;


The third step is to create a map tile layer. Navigate to MapViewer’s Admin console (i.e. go to http://localhost:8888/mapviewer/ , click on the Admin button, log in as a oc4jadmin) and then on the Manage Map Tile Layers tab. Then on Create link in the left panel and then the Continue button. Name the map tile layer, e.g. demo_map_3785, choose mvdemo as the data source and demo_map_3785 as the basemap. Check the transparent checkbox next to the Background color selection box. The default color will be ignored and the map tiles will be rendered on a transparent background. Then set # Zoom Levels to 20 (the number used by Google Maps), ignore the scale level settings, SRID to 3785, Min X to -20037508.3427, Max X to 20037508.3427, Min Y to -20037508.3427, and Max Y to 20037508.3427. Leave the tile size as 256 by 256 and format as PNG. Do NOT click submit. Click XML Mode instead.

To workaround a bug in the UI, which does not let you enter specific tile sizes, we will manually enter this information in XML mode. So click on XML mode and copy and paste the following XML fragment over the existing <zoom levels> … </zoom_levels> fragment. (Note: the following values have been revised and are correct as of April 09 2009)


<zoom_levels levels="20" min_scale="0.0" max_scale="0.0" min_tile_width="76.43702697753906" min_tile_height="4.0075016E7">
<zoom_level level="0" name="" description="" scale="0.0" tile_width="4.0075016E7" tile_height="4.0075016"/>
<zoom_level level="1" name="" description="" scale="0.0" tile_width="2.0037508E7" tile_height="2.0037508E7"/>
<zoom_level level="2" name="" description="" scale="0.0" tile_width="1.0018754E7" tile_height="1.0018754E7"/>
<zoom_level level="3" name="" description="" scale="0.0" tile_width="5009377.0" tile_height="5009377.0"/>
<zoom_level level="4" name="" description="" scale="0.0" tile_width="2504688.5" tile_height="2504688.5"/>
<zoom_level level="5" name="" description="" scale="0.0" tile_width="1252344.25" tile_height="1252344.25"/>
<zoom_level level="6" name="" description="" scale="0.0" tile_width="626172.125" tile_height="626172.125"/>
<zoom_level level="7" name="" description="" scale="0.0" tile_width="313086.0625" tile_height="313086.0625"/>
<zoom_level level="8" name="" description="" scale="0.0" tile_width="156543.03125" tile_height="156543.03125"/>
<zoom_level level="9" name="" description="" scale="0.0" tile_width="78271.515625" tile_height="78271.515625"/>
<zoom_level level="10" name="" description="" scale="0.0" tile_width="39135.7578125" tile_height="39135.7578125"/>
<zoom_level level="11" name="" description="" scale="0.0" tile_width="19567.87890625" tile_height="19567.87890625"/>
<zoom_level level="12" name="" description="" scale="0.0" tile_width="9783.939453125" tile_height="9783.939453125"/>
<zoom_level level="13" name="" description="" scale="0.0" tile_width="4891.9697265625" tile_height="4891.9697265625"/>
<zoom_level level="14" name="" description="" scale="0.0" tile_width="2445.98486328125" tile_height="2445.98486328125"/>
<zoom_level level="15" name="" description="" scale="0.0" tile_width="1222.992431640625" tile_height="1222.992431640625"/>
<zoom_level level="16" name="" description="" scale="0.0" tile_width="611.4962158203125" tile_height="611.4962158203125"/>
<zoom_level level="17" name="" description="" scale="0.0" tile_width="305.74810791015625" tile_height="305.74810791015625"/>
<zoom_level level="18" name="" description="" scale="0.0" tile_width="152.87405395507812" tile_height="152.87405395507812"/>
<zoom_level level="19" name="" description="" scale="0.0" tile_width="76.43702697753906" tile_height="76.43702697753906"/>
</zoom_levels>



Now click the Submit button.

Click on the Manage link and you should see the Existing Map Tile Layers list. Select demo_map_3785 and click on View Map / Manage Tiles. Enter –12000000 for Center X, and 5000000 for Center Y and Show Map. Or just click show map and pan to the left. You should see tiles being generated and displayed. Click return. Below is a screenshot of this new tile layer:



The actual display code


The following sample code uses the tile served up by MapViewer in a Google Maps application. You should save it as tileoverlay-simple.html.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Google Maps JavaScript API Example: Tile Overlays</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAA9WYjhAcvlemZrWK0oPA3ZBSN0IHl8d7gGlihtYuSKk78QOdoAhRRFH3M5iCXxdFN-KENpX8NdJwm0Q"
type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
if (GBrowserIsCompatible()) {

// Set up the copyright information
// Each image used should indicate its copyright permissions
var myCopyright = new GCopyrightCollection("(c) ");
myCopyright.addCopyright(new GCopyright('Demo',
new GLatLngBounds(new GLatLng(-90,-180),
new GLatLng(90,180)), 0,'?2007 Google'));

// Create the tile layer overlay and
// implement the three abstract methods
var tilelayer = new GTileLayer(myCopyright);

tilelayer.getTileUrl = function(tile, zoom) {
var req=null;
req = "http://stadb32.us.oracle.com:7001/mapviewer/mcserver?" +
"request=gettile&format=PNG&"+
"zoomlevel="+(zoom)+"&mapcache=mvdemo.demo_map_3785&mx="
+(tile.x)+"&my="+(Math.pow(2,zoom)-1-tile.y)+"&";
return req;};

tilelayer.isPng = function() { return true;};
tilelayer.getOpacity = function() { return 0.75; }

var myTileLayer = new GTileLayerOverlay(tilelayer);
var map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(33.4419, -90.1419), 4);
map.addControl(new GSmallMapControl());
map.addOverlay(myTileLayer);
}
}
</script>
</head>
<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width: 800px; height: 600px"></div>
</body>
</html>



The relevant (modified) code is the getTileUrl function. Google Maps passes in the tile coordinates for the requested tile and the current zoom level. The parameter “tile” is a GPoint object with “x” and “y” attributes. So tile.x is the x-coordinate for the tile. This is same for Oracle Maps since it also goes from left to right. The y-coordinate value however needs to be recomputed since Google’s Y values increase top to bottom while Oracle’s increase bottom to top. In both cases there are 2Nx2N tiles at zoom level N. So the Y value for the Oracle Maps tile will be (2N –1 – tile.y).


You should modify the Google Maps key as well as the MapViewer server URL in the above code. Save the code as tileoverlay-simple.html, then open it from a browser, and you should see a Google Maps map with the MapViewer tile layer overlay.




Final note, while this article presented how to display MapViewer tiles in a Google Maps mash-up application, the up-coming release of MapViewer (11g R1) will include built-in support for displaying Google Maps and Microsoft Virtual Earth tiles from within your Oracle Maps application. A blog about such support will be posted here as soon as MapViewer 11g R1 is released (which hopefully is just around the corner).