Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
MustafaBensan
Active Contributor

Introduction

This is the follow-up to the blog Design Studio Innovation Series - Topic 6: Geo Maps Part I - Feature Review.  In that blog I covered the main features of the new Geo Map component introduced in Design Studio 1.5.  In this blog I'll cover an approach for zooming and drilling down through multiple regions down to a street-level location and back.

Scenario

The example involves the display of US Census population data loaded into BW, on a map with drill-down from State to County to Place (City or Town).  To demonstrate the presentation of supporting information, there is also a chart with population estimates as well as a table listing the data.  The table can also be used as an alternative method of drilling down through the map.


The end result can be viewed in the following video:

Approach

In a nutshell, the approach used here relies on a combination of:

(a)  Applying the centerMap() method of the GeoMap component;

(b)  Applying filters to the assigned data source;

(c)  Toggling the visibility of map layers.

Steps Covered

  1. GeoJSON File Preparation
  2. Data Preparation
  3. BW Modeling
  4. BEx Query Design
  5. Application Design and Configuration

1.  GeoJSON Map File Preparation

(a) Base Files

To start with we need GeoJSON files representing US states and counties.  I found suitable ones at the GEOJSON AND KML DATA FOR THE UNITED STATES website:

To keep the file size to a minimum I downloaded the lowest resolution versions (20m) as highlighted above, named as follows:

gz_2010_us_040_00_20m.json - States

gz_2010_us_050_00_20m.json - Counties

(b) Merge Map Files

Initially I thought I would need to create a separate map layer for state and county.  However, after reviewing the mapping properties I noticed that conveniently, both files shared a common GeoID property (more on this later) as a key for uniquely mapping to each polygon representing the states and counties, so I thought what if we merged the files in order to use just one map layer?  I chose to combine the two files using the geojson.io tool because my requirement was simple and the tool allowed this to be done without any installation/setup.  Initially you'll see the following home page:

The first task is to simply open or drag and drop the States GeoJSON file onto the map as shown below:

We see a confirmation of the imported features corresponding to the states.  Each feature includes the properties GEO_ID, SATE, NAME, LSAD and CENSUS AREA.  Clicking the Table tab gives us a columnar view of the properties, making it easier to validate what we've imported.

Now we do the same to add the Counties GeoJSON file to the States file we just imported:

By reviewing the Table tab we can see that the counties (with GEO_ID's of prefix 050) have been added after the states (with GEO_ID's of prefix 040):

Now we simply save the combine file in GeoJSON format:

This results in a file of size roughly 4 MB.  I thought I'd try to reduce this a little with the mapshaper tool:

Again, just drag and drop the GeoJSON file onto the browser:

I chose to simplify down to 60%:

After exporting again in GeoJSON format the resulting file is around 3 MB, saving us 1MB which is reasonable for performance.

For further reading about preparation of GeoJSON files I'd recommend the following blogs:

DS15 - Create your own Map by dirk.mayrock, which provides additional tips for using geojson.io  and

Creating Custom GeoJSON maps in Design Studio 1.5 by mike.howles4, which describes QGIS, a more sophisticated tool for creating custom map files.

2.  Data Preparation

The US Census population data is provided summarised at State, County and Place levels so this is what I used to load into BW, with appropriate transformations applied to generate GEO_ID values to match the corresponding ones in the GeoJSON map file.  The raw CSV file looks like this:

This was supplemented with geolocation information for cities and towns.

3.  BW Modeling

The BW infocube and characteristics were modelled as follows:

4.  BEx Query Design

I was able to get away with just two queries:

(a)  US Mainland Population:

Important to note here is that I use a standard BEx user-exit variable to automatically calculate and filter the data for the latest year.  I have also restricted the states to the mainland states (excluding Alaska and offshore territories) to more effectively centre the maps on the screen.

(b)  US Population Estimates:

Note here I've used a standard BEx user-exit variable to calculate and filter the population estimates for the latest 5 years.  Again, I have also restricted the states to the mainland states (excluding Alaska and offshore territories).



5.  Application Design and Configuration


(a) Application Theme


The application applies the Blue Crystal theme as recommended by karol.kalisz in Why Use Blue Crystal Style in Design Studio Apps?



(b) Global Script Variables



(c) Data Sources, Components and Global Script Functions


(d) Data Source Initial Views


(e) Map Layers

Now just a point to note here, for layer US_COUNTY_PLACE_BUBBLE, I did explicitly specify the

Latitude, Longitude and Measure properties but for some reason after saving they no longer displayed but still seemed to be retained by the application.  Perhaps this is a bug.

(f) Scripts

Script for "On Select" event of GEO_MAPP

var geoIDselection = me.getSelectedMember("ZGEOID");

var geoIDselectionKey = geoIDselection.internalKey;

var geoIDselectionText = geoIDselection.text;

GEO_FUNCTIONS.processDrilldown(geoIDselectionKey, geoIDselectionText);

Script for "On Select" event of CROSSTAB_1

var geoIDselection = me.getSelectedMember("ZGEOID");

var geoIDselectionKey = geoIDselection.internalKey;

var geoIDselectionText = geoIDselection.text;

GEO_FUNCTIONS.processDrilldown(geoIDselectionKey, geoIDselectionText);

Script for Global Script Function GEO_FUNCTIONS.processDrilldown

var geoIDselectionKey = pGeoIDkey;

var geoIDselectionText = pGeoIDtext;

var geoType = GEO_FUNCTIONS.getGeoType(geoIDselectionKey);

if (currentLevel < levelCounty) {

  if (geoType==stateGeoType) {

  currentLevel = currentLevel + 1;

  currentState = GEO_FUNCTIONS.getStateFIPS(geoIDselectionKey);

  currentStateGeoID = geoIDselectionKey;

  currentStateText = geoIDselectionText;

  mapTitle = currentStateText;

  DS_1.setFilter("ZSUMLEV", countyLevel);

  DS_1.setFilter("ZSTATEFIP", currentState);

  DS_3.clearFilter("ZSUMLEV");

  DS_3.setFilter("ZGEOID", currentStateGeoID);

  GEO_MAP.centerMap(stateCountyMapLayerID);

  FIORIAPPHEADER.setShowNavButton(true);

  } else if ( geoType == countyGeoType )

  {

  currentLevel = currentLevel + 1;

  currentCounty = GEO_FUNCTIONS.getCountyFIPS(geoIDselectionKey);

  currentCountyGeoID = geoIDselectionKey;

  currentCountyText = geoIDselectionText;

  mapTitle = currentCountyText + ", " + mapTitle;

  DS_1.setFilter("ZGEOID",currentCountyGeoID);

  DS_3.setFilter("ZGEOID",currentCountyGeoID);

  GEO_MAP.centerMap(stateCountyMapLayerID);

  DS_2.setFilter("ZCNTYFIP", currentCounty );

  GEO_MAP.setLayerVisible(countyPlaceBubbleLayerID, true);

  CROSSTAB_1.setDataSource(DS_2);

  CROSSTAB_1.resetAllColumnWidths();

  }

} else if (currentLevel == levelCounty) {

  currentLevel = currentLevel + 1;

  currentPlaceGeoID = geoIDselectionKey;

  currentPlaceText = geoIDselectionText;

  mapTitle = currentPlaceText + ", " + mapTitle;

  DS_2.setFilter("ZGEOID", currentPlaceGeoID);

  DS_3.setFilter("ZGEOID",currentPlaceGeoID);

  GEO_MAP.setLayerVisible(stateCountyMapLayerID, false);

  GEO_MAP.centerMap(countyPlaceBubbleLayerID);

}

GEO_FUNCTIONS.setMapTitle(mapTitle, year);

Script for "On Back" event of FIORIAPPHEADER

Here we navigate back or drill up.

if (currentLevel == levelPlace) {

  currentLevel = currentLevel - 1;

  currentPlace = "";

  currentPlaceGeoID = "";

  mapTitle = currentCountyText + ", " + currentStateText;

  GEO_FUNCTIONS.setMapTitle(mapTitle, year);

  GEO_MAP.setLayerVisible(stateCountyMapLayerID,true);

  GEO_MAP.centerMap(stateCountyMapLayerID);

  DS_2.clearFilter("ZGEOID");

  DS_3.setFilter("ZGEOID", currentCountyGeoID);

} else if (currentLevel == levelCounty) {

  currentLevel = currentLevel - 1;

  currentCounty = "";

  currentCountyGeoID = "";

  mapTitle = currentStateText;

  GEO_FUNCTIONS.setMapTitle(mapTitle, year);

  GEO_MAP.setLayerVisible(countyPlaceBubbleLayerID,false);

  DS_1.clearFilter("ZGEOID");

  GEO_MAP.centerMap(stateCountyMapLayerID);

  CROSSTAB_1.setDataSource(DS_1);

  DS_3.setFilter("ZGEOID", currentStateGeoID);

} else if (currentLevel == levelState) {

  FIORIAPPHEADER.setShowNavButton(false);

  currentLevel = currentLevel - 1;

  currentState = "";

  currentStateGeoID = "";

  mapTitle = defaultMapTitle;

  GEO_FUNCTIONS.setMapTitle(mapTitle, year);

  DS_1.clearFilter("ZSTATEFIP");

  DS_1.setFilter("ZSUMLEV",stateLevel);

  GEO_MAP.centerMap(stateCountyMapLayerID);

  DS_3.clearFilter("ZGEOID");

  DS_3.setFilter("ZSUMLEV", "040");

}

Script for "On Item Select" event of FIORIAPPHEADER

Here we select between the GeoMap and Analytic Map (no basemap)

var mapType = me.getItemClicked();

if (mapType == "GeoMap") {

GEO_MAP.setMapUrl(basemapURL);

}

else {GEO_MAP.setMapUrl("");}

Conclusion

I hope the example presented here has given you a good idea of the creative possibilities with the GeoMap component.  There's quite a lot more I would have liked to explain based on my experiences preparing this post but that's probably best left to a lessons learned follow-up.

Comments and questions are most welcome as usual.

Blog Series Index:  Design Studio Innovation Series - Welcome

41 Comments
Labels in this area