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).

Monday, January 26, 2009

Heat map support in MapViewer 11g

One of the many new features of Oracle MapViewer 11g (which is now in the final testing stage) is heat maps. If you don't know what exactly is a heat map, it is a color based 2D representation of a point data set's distribution pattern. It is great at revealing clusters of points or events that occur in a study region.

Here is a screen shot of such a heat map fully integrated with the Oracle Maps JavaScript API.



The above map displays the CITIES table in MapViewer's MVDEMO data set as a heat map. The color gradients (from white to green to orange and finally to red) shows the distribution pattern (clusters) of cities and towns of US. In MapViewer terms, the CITIES theme is rendered using a HEAT MAP style. Internal tests have shown MapViewer can easily generate a heat map from hundreds of thousands of points, on the fly.

Here is another screen shot, this time showing the distribution of pizza restaurants across the states.

Wednesday, January 21, 2009

Creating a separate MapViewer admin account

By default MapViewer borrows the same admin account that you use to log into the Application Server admin web page. For OracleAS 10.1.3.* this is typically the user named "oc4jadmin", and for WebLogic it's typically "weblogic".

In some cases however you may want to create a separate admin account just for MapViewer,such as when you do not wish to give away the middleware's master admin account information. Basically, this new account can only be used to log into MapViewer's admin page and perform all of its management tasks. It cannot be used to log into the middleware's master admin web site (the /em URL for OracleAS 10.1.3.* or the /console URL for WebLogic Server).

So how do you do that? We will show you the steps for both OracleAS 10.1.3 and WebLogic Server 10.

Oracle Application Server 10.1.3.*

Lets first assume you already have MapViewer deployed to one of the OC4J instances in your OracleAS 10.1.3.* installation. If MapViewer is not deployed yet, you should go ahead and deploy it.

Step 1. Log onto App Server's admin web site (/em).
Step 2. Find the OC4J instance that contains MapViewer. Then click on its Administration tab.
Step 3. Find the Security - Security Providers row, then click the Go to Task icon. This is shown in the following screen shot.



Step 4. Click the "Instance Level Security" button
Step 5. Select the Realms tab
Step 6. Click the number in the Users column of the row starting with "jazn.com".
Step 7. Click the Create button.
Step 8. Enter the user name and password for the new user to be created for MapViewer. For example I entered mapadmin as the user name.
Step 9. On the same page, from Available Roles, select and move "oc4j-app-administrators" to the Selected Roles box. Click Ok. You should see a confirmation page showing the new user and its assigned roles (oc4j-app-administrators).

The above steps create a new admin user for MapViewer with the assigned role of oc4j-app-administrators. We now need to modify Mapviewer's deployment descriptor so that it will accept this role for administration purposes.

Modify MapViewer's deployment descriptor

Before performing the following step, I typically shutdown the entire OracleAS instance; however it may also work if you just shut down the OC4J instance that contains MapViewer.

Once the OC4J instance is down, edit the following MapViewer deployment descriptor file:

AS_HOME/j2ee/home/application-deployments/mapviewer/web/orion-web.xml

where AS_HOME is the home directory of your OracleAS install. If your MapViewer is NOT deployed to the "home" OC4J instance, then you will also need to replace "home" with the directory name of the actual OC4J instance containing MapViewer. If your OC4J is named "mapper" for instance, then the direcorry is typically AS_HOME/j2ee/oc4j_mapper/...

Once the orion-web.xml file is opened in an editor, locate the XML element <security-role-mapping name="map_admin_role">. Add a new child <group> element
<group name="oc4j-app-administrators">. In other words, the entire element should look like this:

<security-role-mapping name="map_admin_role">
<group name="oc4j-app-administrators"/>
<group name="oc4j-administrators"/>
<group name="administrators"/>
</security-role-mapping>


You may also find another tag <security-role-mapping name="secure_maps_role"> in the same file, leave that one alone (it is used for the secure mapping demos).

By making the above change, we are telling MapViewer that any App Server user with the role "oc4j-app-administrators" should also be considered a MapViewer admin user.

Save the file. And restart OC4J or OracleAS instance.

Now you should be able to log into MapViewer's admin page using the newly created user (mapadmin in my case). All the admin tasks of MapViewer should go through as usual. You will not be able to log into the App Server admin site (/em) using mapadmin. You can however, still log into MapViewer's admin page using the oc4jadmin account (after all it is the master admin for the entire OracleAS instance).

WebLogic Server

To be added later...

Monday, November 24, 2008

Official MapViewer white paper: a primer

Just FYI the official MapViewer Primer article can be found here:

http://www.oracle.com/technology/products/mapviewer/mapviewer_training_index.html

It provides a good and concise introduction on MapViewer.

Wednesday, November 19, 2008

Creating a web map from raster image files

This article outlines the steps needed to create a "slippy" web map from a set of raster image files on your disk. The main steps are:

  1. load the image files into Oracle Spatial and store them as a GeoRaster table

  2. create a MapViewer theme and base map for the GeoRaster table

  3. create a MapViewer map tile layer so that the AJAX mapping client can display the data as a slippy map in the browser


Lets now go through these steps with a bit more detail.

Loading image files into Oracle Spatial

For this we will use the Map Builder tool. If you don't have the latest version of Map Builder, you can get it from OTN MapViewer page. The latest kit is MapViewer version 10.1.3.3 for WebLogic.

Fire up Map Builder, then go to Tools > Import Image. You will be prompted to enter the name for a GeoRaster table to be created (if you are importing an image for the first time), and a few other things including the raster data table name and georaster column name. You should jot down these names as you will need to enter them again when importing a second image and so forth.



Click Next. You can accept default values for all the fields. Note that the SRID for the georaster data is set to 999999 by default, you should change it if you know the exact Oracle SRID for the image files you are importing. If you do not know the SRID, you should set it to 262148 here. SRID 262148 is a meter-based local coordinate system, and will ensure you can create a tile layer in the last step. Note that you can always update it later to a more accurate SRID.



Click Next. Then click Finish. Map Builder will start uploading the image file to the database.




After the image is uploaded and stored in the GeoRaster table, Map Builder will automatically prompt you to create a GeoRaster theme based on the table. A GeoRaster theme is required by MapViewer to visualize your image data. So click Next if this is the first image you just uploaded and there is no GeoRaster theme exists for it.




Enter a name for your new GeoRaster theme, then click Next. Click Next also on the 2nd and 3rd screen of this dialog. Then click Finish. You now have a GeoRaster theme for the image you just uploaded. You can preview this theme in Map Builder to ensure everything so far is working (in other words, you should see your image which is now stored in the database).


If you have more than one image files, just repeat the above steps. Note that you should use the same GeoRaster table name, column name and Raster Data Table name for all subsequent image files. This will ensure that all the image files are stored in the same GeoRaster table in Oracle Spatial. It will also make sure Map Viewer automatically "stitch" your image files when visualizing them. After finishing uploading each image file, MapBuilder will prompt again and ask if you want to create a GeoRaster theme. Click Cancel here since you already created the theme, which will automatically see new image data added to the GeoRaster table.


Creating a MapViewer base map

Now that all of your image files are stored neatly as a GeoRaster table in Oracle Spatial, and MapViewer is (partially) happy because you created a theme based on that table, there remains two more steps for the data to be displayable in a "slippy" map in your browser. First, we need to create a MapViewer Base map that includes/references this theme. Second we need to create a map tile layer which is what the slippy mapping client is expecting.

To create a base map, we will again use Map Builder. Right click on the Base Maps node in the left panel of Map Builder. In the ensuing dialog, enter a name for the new base map. Click Next. In the second screen as shown below, find and select the theme we just created, and click the button with a "+" icon to add the theme to the new base map.





Click Next and Finish. This will create a MapViewer base map that contains just the GeoRaster theme. Again you should be able to preview the base map inside Map Builder and see the same image data.

Creating a Map Tile Layer


The slippy Oracle Maps map cannot directly display a MapViewer base map for legacy reasons. It expects a MapViewer tile layer instead. So we need to create a map tile layer out of the base map. For this you will need to use the MapViewer Admin web page. So point your browser to the MapViewer home page, click the Admin icon/button, and login to the Admin page. Click the Management tab, then Manage Map Tile Layers. Click Create, select Internal. Click Continue. And you will see a web page that defines all aspects of the new Tile Layer, such as its name, what is the base map, and so forth. This is illustrated in the following screen shot:





Note that you will need to make sure proper values are entered for the fields Min X, Min Y, Max X, Max Y, Minimum Map Scale, Max Map Scale and SRID. The fields Min X, Min Y, Max X and Max Y define the coordinates of an imaginary bounding box that covers all the imported images. You should be able to get these values by checking the coordinates in the TFW files. Or you can use Map Builder's preview feature to get a rough estimate since the preview panel displays the coordinate info too.

Min and Max Map Scale define the smallest and largest map scales that the map images should be displayed (or will display reasonablly well in terms of clarity). They are true map scales and you can experiment with different values to get a sense of how they affect the user experience. A value of 1000 means the map scale is 1:1000, which in turn means a unit of distance (say an inch) on the screen represents 1000 same units (inches) on the ground.

The other field, # Zoom Levels, defines how many steps to go from zoomed way out (Max Map Scale) to way in (Min Map Scale). Again, it is okay to experiment with all of these values in case you didnt get it all right the first time. It's not a big deal to delete the current one and start over with a new map tile layer.

Finally, the SRID field should contain the same SRID value you entered when importing images in Map Builder.

Testing your map


So how do you actually know if your map tile layer is created properly? You display it in a simple HTML page! The easiest way is to copy and modify one of the existing Oracle Maps tutorials (each tutorial is one very simple HTML/JavaScript page). Simply replace the map tile name with the one you just created, and set the proper map center point and zoom level for your data. Open the page in your browser and you should see a draggable slippy map showing your image data!