Creating a thematic map

Posted by jsharma on Oracle Blogs See other posts from Oracle Blogs or by jsharma
Published on Wed, 26 Jun 2013 19:40:12 +0000 Indexed on 2013/06/26 22:26 UTC
Read the original article Hit count: 333

Filed under:

This post describes how to create a simple thematic map, just a state population layer, with no underlying map tile layer. The map shows states color-coded by total population. The map is interactive with info-windows and can be panned and zoomed.

The sample code demonstrates the following:

  • Displaying an interactive vector layer with no background map tile layer (i.e. purpose and use of the Universe object)
  • Using a dynamic (i.e. defined via the javascript client API) color bucket style
  • Dynamically changing a layer's rendering style
  • Specifying which attribute value to use in determining the bucket, and hence style, for a feature (FoI)

The result is shown in the screenshot below.

Screenshot of US state population thematic map


The states layer was defined, and stored in the user_sdo_themes view of the mvdemo schema, using MapBuilder. The underlying table is defined as

SQL> desc states_32775
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 STATE                                              VARCHAR2(26)
 STATE_ABRV                                         VARCHAR2(2)
 FIPSST                                             VARCHAR2(2)
 TOTPOP                                             NUMBER
 PCTSMPLD                                           NUMBER
 LANDSQMI                                           NUMBER
 POPPSQMI                                           NUMBER
...
 MEDHHINC                                           NUMBER
 AVGHHINC                                           NUMBER
 GEOM32775                                          MDSYS.SDO_GEOMETRY

We'll use the TOTPOP column value in the advanced (color bucket) style for rendering the states layers. The predefined theme (US_STATES_BI) is defined as follows.

SQL> select styling_rules from user_sdo_themes where name='US_STATES_BI';

STYLING_RULES
--------------------------------------------------------------------------------

<?xml version="1.0" standalone="yes"?>
<styling_rules highlight_style="C.CB_QUAL_8_CLASS_DARK2_1">
  <hidden_info>
    <field column="STATE" name="Name"/>
    <field column="POPPSQMI" name="POPPSQMI"/>
    <field column="TOTPOP" name="TOTPOP"/>
  </hidden_info>
  <rule column="TOTPOP">
    <features style="states_totpop"> </features>
    <label column="STATE_ABRV" style="T.BLUE_SERIF_10"> 1 </label>
  </rule>
</styling_rules>

SQL>

The theme definition specifies that the state, poppsqmi, totpop, state_abrv, and geom columns will be queried from the states_32775 table. The state_abrv value will be used to label the state while the totpop value will be used to determine the color-fill from those defined in the states_totpop advanced style. The states_totpop style, which we will not use in our demo, is defined as shown below.

SQL> select definition from user_sdo_styles where name='STATES_TOTPOP';

DEFINITION
--------------------------------------------------------------------------------
<?xml version="1.0" ?>
<AdvancedStyle>
   <BucketStyle>
    <Buckets default_style="C.S02_COUNTRY_AREA">
     <RangedBucket seq="0" label="10K - 5M" low="10000" high="5000000" style="C.SEQ6_01" />
      <RangedBucket seq="1" label="5M - 12M" low="5000001" high="1.2E7" style="C.SEQ6_02" />
      <RangedBucket seq="2" label="12M - 20M" low="1.2000001E7" high="2.0E7" style="C.SEQ6_04" />
      <RangedBucket seq="3" label="&gt; 20M" low="2.0000001E7" high="5.0E7" style="C.SEQ6_05" />
    </Buckets>
   </BucketStyle>
</AdvancedStyle>

SQL>

The demo defines additional advanced styles via the OM.style object and methods and uses those instead when rendering the states layer.  

Now let's look at relevant snippets of code that defines the map extent and zoom levels (i.e. the OM.universe),  loads the states predefined vector layer (OM.layer), and sets up the advanced (color bucket) style.

Defining the map extent and zoom levels.
function initMap()
{
  //alert("Initialize map view");
  
  // define the map extent and number of zoom levels.
  // The Universe object is similar to the map tile layer configuration
  // It defines the map extent, number of zoom levels, and spatial reference system
  // well-known ones (like web mercator/google/bing or maps.oracle/elocation are predefined
  // The Universe must be defined when there is no underlying map tile layer. 
  // When there is a map tile layer then that defines the map extent, srid, and zoom levels.
     var uni= new OM.universe.Universe(
    {
        srid : 32775,
        bounds : new OM.geometry.Rectangle(
                        -3280000, 170000, 2300000, 3200000, 32775),
        numberOfZoomLevels: 8
    });

The srid specifies the spatial reference system which is Equal-Area Projection (United States).

SQL> select cs_name from cs_srs where srid=32775 ;
CS_NAME
---------------------------------------------------
Equal-Area Projection (United States)

The bounds defines the map extent. It is a Rectangle defined using the lower-left and upper-right coordinates and srid.

Loading and displaying the states layer

This is done in the states() function. The full code is at the end of this post, however here's the snippet which defines the states VectorLayer.

    // States is a predefined layer in user_sdo_themes
    var  layer2 = new OM.layer.VectorLayer("vLayer2", 
    {
        def:
        {
            type:OM.layer.VectorLayer.TYPE_PREDEFINED, 
            dataSource:"mvdemo", 
            theme:"us_states_bi", 
            url: baseURL,
            loadOnDemand: false
        },
        boundingTheme:true
     });

The first parameter is a layer name, the second is an object literal for a layer config. The config object has two attributes: the first is the layer definition, the second specifies whether the layer is a bounding one (i.e. used to determine the current map zoom and center such that the whole layer is displayed within the map window) or not. The layer config has the following attributes:

type - specifies whether is a predefined one, a defined via a SQL query (JDBC), or in a json-format file (DATAPACK)

theme - is the predefined theme's name

url - is the location of the mapviewer server

loadOnDemand - specifies whether to load all the features or just those that lie within the current map window and load additional ones as needed on a pan or zoom

The code snippet below dynamically defines an advanced style and then uses it, instead of the 'states_totpop' style, when rendering the states layer.

// override predefined rendering style with programmatic one
   var theRenderingStyle = 
     createBucketColorStyle('YlBr5', colorSeries, 'States5', true);
  // specify which attribute is used in determining the bucket (i.e. color) to use for the state
  // It can be an array because the style could be a chart type (pie/bar)
  // which requires multiple attribute columns   
  // Use the STATE.TOTPOP column (aka attribute) value here
   layer2.setRenderingStyle(theRenderingStyle, ["TOTPOP"]);

The style itself is defined in the createBucketColorStyle() function.

Dynamically defining an advanced style

The advanced style used here is a bucket color style, i.e. a color style is associated with each bucket. So first we define the colors and then the buckets. 

    numClasses = colorSeries[colorName].classes;
   // create Color Styles
   for (var i=0; i < numClasses; i++) 
   {
        theStyles[i] = new OM.style.Color(
                     {fill: colorSeries[colorName].fill[i], 
                       stroke:colorSeries[colorName].stroke[i],
                      strokeOpacity: useGradient? 0.25 : 1
                     });
   };

numClasses is the number of buckets. The colorSeries array contains the color fill and stroke definitions and is:

var colorSeries = {
//multi-hue color scheme #10 YlBl. 
"YlBl3": {   classes:3,
                 fill: [0xEDF8B1, 0x7FCDBB, 0x2C7FB8],
                 stroke:[0xB5DF9F, 0x72B8A8, 0x2872A6]
  },
"YlBl5": {   classes:5,
                 fill:[0xFFFFCC, 0xA1DAB4, 0x41B6C4, 0x2C7FB8, 0x253494],
                 stroke:[0xE6E6B8, 0x91BCA2, 0x3AA4B0, 0x2872A6, 0x212F85]
  },
//multi-hue color scheme #11 YlBr.
 "YlBr3": {classes:3,
                 fill:[0xFFF7BC, 0xFEC44F, 0xD95F0E],
                 stroke:[0xE6DEA9, 0xE5B047, 0xC5360D] 
  },
"YlBr5": {classes:5,
                 fill:[0xFFFFD4, 0xFED98E, 0xFE9929, 0xD95F0E, 0x993404],
                 stroke:[0xE6E6BF, 0xE5C380, 0xE58A25, 0xC35663, 0x8A2F04]
    },

etc.

Next we create the bucket style.

   bucketStyleDef = {
      numClasses : colorSeries[colorName].classes,
//      classification: 'custom',  //since we are supplying all the buckets
//      buckets: theBuckets,
      classification: 'logarithmic',  // use a logarithmic scale 
      styles: theStyles,
      gradient:  useGradient? 'linear' : 'off'
//      gradient:  useGradient? 'radial' : 'off'
    };
   theBucketStyle = new OM.style.BucketStyle(bucketStyleDef);
   return theBucketStyle;

A BucketStyle constructor takes a style definition as input. The style definition specifies the number of buckets (numClasses), a classification scheme (which can be equal-ranged, logarithmic scale, or custom), the styles for each bucket, whether to use a gradient effect, and optionally the buckets (required when using a custom classification scheme).

The full source for the demo
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Oracle Maps V2 Thematic Map Demo</title>

<script src="http://localhost:8080/mapviewer/jslib/v2/oraclemapsv2.js" type="text/javascript">
</script>

<script type="text/javascript">
//var $j = jQuery.noConflict();
var baseURL="http://localhost:8080/mapviewer";  // location of mapviewer
OM.gv.proxyEnabled =false;			// no mvproxy needed
OM.gv.setResourcePath(baseURL+"/jslib/v2/images/");  // location of resources for UI elements like nav panel buttons
var map = null;											 // the client mapviewer object 
var statesLayer = null, stateCountyLayer = null;		 // The vector layers for states and counties in a state
var layerName="States";
// initial map center and zoom
var mapCenterLon = -20000;
var mapCenterLat =  1750000;
var mapZoom      =  2;  
var mpoint = new OM.geometry.Point(mapCenterLon,mapCenterLat,32775);
var currentPalette = null, currentStyle=null;

// set an onchange listener for the color palette select list
// initialize the map
// load and display the states layer
$(document).ready( function()
{

      $("#demo-htmlselect").change(function() {
			var theColorScheme = $(this).val();
			useSelectedColorScheme(theColorScheme);
		});
      initMap();
      states();	  
	  
}
);

/**
 * color series from ColorBrewer site (http://colorbrewer2.org/).
 */

var colorSeries = {
  
//multi-hue color scheme #10 YlBl.
  
"YlBl3": {   classes:3,
                 fill: [0xEDF8B1, 0x7FCDBB, 0x2C7FB8],
                 stroke:[0xB5DF9F, 0x72B8A8, 0x2872A6]
  },
  
"YlBl5": {   classes:5,
                 fill:[0xFFFFCC, 0xA1DAB4, 0x41B6C4, 0x2C7FB8, 0x253494],
                 stroke:[0xE6E6B8, 0x91BCA2, 0x3AA4B0, 0x2872A6, 0x212F85]
  },
  
//multi-hue color scheme #11 YlBr.
 
 "YlBr3": {classes:3,
                 fill:[0xFFF7BC, 0xFEC44F, 0xD95F0E],
                 stroke:[0xE6DEA9, 0xE5B047, 0xC5360D] 
  },
  
"YlBr5": {classes:5,
                 fill:[0xFFFFD4, 0xFED98E, 0xFE9929, 0xD95F0E, 0x993404],
                 stroke:[0xE6E6BF, 0xE5C380, 0xE58A25, 0xC35663, 0x8A2F04]
    },
// single-hue color schemes (blues, greens, greys, oranges, reds, purples)
"Purples5": {classes:5,
                 fill:[0xf2f0f7, 0xcbc9e2, 0x9e9ac8, 0x756bb1, 0x54278f],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    },
"Blues5": {classes:5,
                 fill:[0xEFF3FF, 0xbdd7e7, 0x68aed6, 0x3182bd, 0x18519C],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    },
"Greens5": {classes:5,
                fill:[0xedf8e9, 0xbae4b3, 0x74c476, 0x31a354, 0x116d2c],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    },  
"Greys5": {classes:5,
                 fill:[0xf7f7f7, 0xcccccc, 0x969696, 0x636363, 0x454545],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    },
"Oranges5": {classes:5,
                 fill:[0xfeedde, 0xfdb385, 0xfd8d3c, 0xe6550d, 0xa63603],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    },
"Reds5": {classes:5,
                 fill:[0xfee5d9, 0xfcae91, 0xfb6a4a, 0xde2d26, 0xa50f15],
                 stroke:[0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3, 0xd3d3d3]
    }
};

function createBucketColorStyle(
colorName, colorSeries, rangeName, useGradient)
{
   var theBucketStyle;
   var bucketStyleDef;
   var theStyles = [];
   var theColors = [];
   var aBucket, aStyle, aColor, aRange;
   var numClasses ;

   numClasses = colorSeries[colorName].classes;

   // create Color Styles
   for (var i=0; i < numClasses; i++) 
   {
 
        theStyles[i] = new OM.style.Color(
                     {fill: colorSeries[colorName].fill[i], 
                       stroke:colorSeries[colorName].stroke[i],
                      strokeOpacity: useGradient? 0.25 : 1
                     });
   };

   bucketStyleDef = {
      numClasses : colorSeries[colorName].classes,
//      classification: 'custom',  //since we are supplying all the buckets
//      buckets: theBuckets,
      classification: 'logarithmic',  // use a logarithmic scale 
      styles: theStyles,
      gradient:  useGradient? 'linear' : 'off'
//      gradient:  useGradient? 'radial' : 'off'
    };


   theBucketStyle = new OM.style.BucketStyle(bucketStyleDef);


   return theBucketStyle;
}

function initMap()
{
  //alert("Initialize map view");
  
  // define the map extent and number of zoom levels.
  // The Universe object is similar to the map tile layer configuration
  // It defines the map extent, number of zoom levels, and spatial reference system
  // well-known ones (like web mercator/google/bing or maps.oracle/elocation are predefined
  // The Universe must be defined when there is no underlying map tile layer. 
  // When there is a map tile layer then that defines the map extent, srid, and zoom levels.
     var uni= new OM.universe.Universe(
	{
		srid : 32775,
		bounds : new OM.geometry.Rectangle(
                        -3280000, 170000, 2300000, 3200000, 32775),
		numberOfZoomLevels: 8
	});
  map = new OM.Map(
    	document.getElementById('map'),
    	{
          mapviewerURL: baseURL,
    	  universe:uni
    	}) ;
        
  var navigationPanelBar = new OM.control.NavigationPanelBar();
  map.addMapDecoration(navigationPanelBar);
} // end initMap

function states()
{
  
  //alert("Load and display states");
     layerName = "States";
     
     if(statesLayer) 
     { 
       // states were already visible but the style may have changed
       // so set the style to the currently selected one
       var theData = $('#demo-htmlselect').val();
       setStyle(theData); 
     }
     else 
     {
     // States is a predefined layer in user_sdo_themes
     var  layer2 = new OM.layer.VectorLayer("vLayer2", 
        {
          def:
          {
			type:OM.layer.VectorLayer.TYPE_PREDEFINED, 
			dataSource:"mvdemo", 
			theme:"us_states_bi", 
			url: baseURL,
			loadOnDemand: false
  	      },
		boundingTheme:true
  	  });

   // add drop shadow effect and hover style
   var shadowFilter = new OM.visualfilter.DropShadow({opacity:0.5, color:"#000000", offset:6, radius:10});


   var hoverStyle = new OM.style.Color(
        {stroke:"#838383", strokeThickness:2});

   layer2.setHoverStyle(hoverStyle);
   layer2.setHoverVisualFilter(shadowFilter);

   layer2.enableFeatureHover(true);

   layer2.enableFeatureSelection(false);
   layer2.setLabelsVisible(true);
 
// override predefined rendering style with programmatic one

   var theRenderingStyle = 
     createBucketColorStyle('YlBr5', colorSeries, 'States5', true);

  // specify which attribute is used in determining the bucket (i.e. color) to use for the state
  // It can be an array because the style could be a chart type (pie/bar)
  // which requires multiple attribute columns   
  // Use the STATE.TOTPOP column (aka attribute) value here
   layer2.setRenderingStyle(theRenderingStyle, ["TOTPOP"]);
  
   currentPalette = "YlBr5";

   var stLayerIdx =   map.addLayer(layer2);
   //alert('State Layer Idx = ' + stLayerIdx);

   map.setMapCenter(mpoint);
  
   map.setMapZoomLevel(mapZoom) ;
   
   // display the map
   map.init() ;

   statesLayer=layer2;

   // add rt-click event listener to show counties for the state
   layer2.addListener(OM.event.MouseEvent.MOUSE_RIGHT_CLICK,stateRtClick);
   } // end if 

} // end states

function setStyle(styleName) 
{
  // alert("Selected Style = " + styleName);

  // there may be a counties layer also displayed.
  // that wll have different bucket ranges so create 
  // one style for states and one for counties
  var newRenderingStyle = null; 
  if (layerName === "States") 
  {
    if(/3/.test(styleName)) 
    {
     newRenderingStyle = 
     createBucketColorStyle(styleName, colorSeries, 'States3', false);
	 currentStyle = 
	 createBucketColorStyle(styleName, colorSeries, 'Counties3', false);
    }
    else 
    {
     newRenderingStyle = 
     createBucketColorStyle(styleName, colorSeries, 'States5', false);
	 currentStyle = 
	 createBucketColorStyle(styleName, colorSeries, 'Counties5', false);
    }   
    statesLayer.setRenderingStyle(newRenderingStyle, ["TOTPOP"]);
	if (stateCountyLayer)
	 stateCountyLayer.setRenderingStyle(currentStyle, ["TOTPOP"]);
  }
} // end setStyle

function stateRtClick(evt){
  var foi = evt.feature;
  //alert('Rt-Click on State: ' + foi.attributes['_label_'] + 
  //      ' with pop ' + foi.attributes['TOTPOP']);

  // display another layer with counties info 

  // layer may change on each rt-click so create and add each time.
  var countyByState = null ;
  // the _label_ attribute of a feature in this case is the state abbreviation
  // we will use that to query and get the counties for a state
  var sqlText =
"select totpop,geom32775 from counties_32775_moved where state_abrv="+
      "'"+foi.getAttributeValue('_label_')+"'";


// alert(sqlText);

  if (currentStyle === null)
    currentStyle = 
     createBucketColorStyle('YlBr5', colorSeries, 'Counties5', false);
  /* try a simple style instead   
    new OM.style.ColorStyle(
		   {
                      stroke: "#B8F4FF",
		      fill: "#18E5F4",
                      fillOpacity:0
	           }
     );
   */
  // remove existing layer if any
  if(stateCountyLayer) 
    map.removeLayer(stateCountyLayer);

  countyByState = new OM.layer.VectorLayer("stCountyLayer", 
                  {def:{type:OM.layer.VectorLayer.TYPE_JDBC,
                   dataSource:"mvdemo",
                   sql:sqlText,
                   url:baseURL}});			   
//                   url:baseURL},
//                   renderingStyle:currentStyle});

  countyByState.setVisible(true);
  // specify which attribute is used in determining the bucket (i.e. color) to use for the state
  countyByState.setRenderingStyle(currentStyle, ["TOTPOP"]);

   var ctLayerIdx =   map.addLayer(countyByState);
   // alert('County Layer Idx = ' + ctLayerIdx);

  //map.addLayer(countyByState);
  stateCountyLayer = countyByState;
} // end stateRtClick

function useSelectedColorScheme(theColorScheme)
{
   if(map) 
   {
      // code to update renderStyle goes here
	  //alert('will try to change render style');
	  setStyle(theColorScheme);
   }
   else
   {
    // do nothing 
   }
}

</script>
</head>

<body bgcolor="#b4c5cc" style="height:100%;font-family:Arial,Helvetica,Verdana">

<h3 align="center">State population thematic map </h3>
<div id="demo" style="position:absolute; left:68%; top:44px; width:28%; height:100%">
<HR/>
<p/>
Choose Color Scheme:
<select id="demo-htmlselect">
<option value="YlBl3">
YellowBlue3</option>
<option value="YlBr3">
YellowBrown3</option>
<option value="YlBl5">
YellowBlue5</option>
<option value="YlBr5" selected="selected">
YellowBrown5</option>
<option value="Blues5">
Blues</option>
<option value="Greens5">
Greens</option>
<option value="Greys5">
Greys</option>
<option value="Oranges5">
Oranges</option>
<option value="Purples5">
Purples</option>
<option value="Reds5">
Reds</option>
</select>
<p/>

</div>
<div id="map" style="position:absolute; left:10px; top:50px; width:65%; height:75%; background-color:#778f99"></div>
<div style="position:absolute;top:85%; left:10px;width:98%" class="noprint">
<HR/>
<p> Note: This demo uses HTML5 Canvas and requires IE9+, Firefox 10+, or Chrome. No map will show up in IE8 or earlier.
</p>
</div>
</body>
</html>


© Oracle Blogs or respective owner

Related posts about /Oracle