| Oracle® Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド 11gリリース1(11.1.1.9.0) B52029-12 |
|
![]() 前 |
![]() 次 |
この付録では、このガイド全体の項で参照されている、完全な長さのコード・サンプルを示します。
この付録の内容は次のとおりです。
次は、DVTマップ・コンポーネントを作成するサンプル・コードです。
テーマ・マップ用のカスタム・ベース・マップを作成する場合は、oracle.adf.view.faces.bi.component.thematicMap.mapProvider.MapProviderを拡張します。次に示すのは、カナダのカスタム・ベース・マップ例のためのmapProviderクラスの実装です。
package oracle.adfdemo.view.feature.rich.thematicMap;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipInputStream;
import javax.faces.context.FacesContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.MapProvider;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.utils.MapProviderUtils;
/**
* Sample MapProvider instance that translates GeoJSON data from a zip file to create a custom Canada basemap.
*/
public class DemoMapProvider extends MapProvider {
private static String zipFile = "/resources/thematicMap/elocation.zip";
private static final ADFLogger _logger = ADFLogger.createADFLogger(DemoMapProvider.class);
private List<String> hierarchy;
public DemoMapProvider() {
hierarchy = new ArrayList<String>();
hierarchy.add("states");
}
@Override
public Collection<LayerArea> getLayerAreas(String layer, Locale locale) {
String text = getGeoJsonString();
Map geoJSON = null;
try {
geoJSON = (Map) DemoMapProvider.parseJsonString(text);
} catch (IOException e) {
_logger.severe("Could not parse geometries.", e);
}
return DemoMapProvider.createLayerAreas(geoJSON);
}
@Override
public Collection<LayerArea> getChildAreas(String layer, Locale locale, Collection<String> areas) {
return new ArrayList<LayerArea>();
}
@Override
public double getMaxZoomFactor() {
return 5.0;
}
@Override
public List<String> getHierarchy() {
return hierarchy;
}
/**
* Loads the geographic geometries stored in local file and returns as string
* @return
*/
private static String getGeoJsonString() {
StringBuilder sb = new StringBuilder();
BufferedReader br = null;
ZipInputStream is = null;
try {
URL zipUrl = FacesContext.getCurrentInstance().getExternalContext().getResource(zipFile);
is = new ZipInputStream(zipUrl.openStream());
is.getNextEntry();
br = new BufferedReader(new InputStreamReader(is, "UTF8"));
String aux = br.readLine();
while (aux != null) {
sb.append(aux);
aux = br.readLine();
}
} catch (Exception e) {
_logger.severe("Could not load geometries from the file " + zipFile, e);
} finally {
if (br != null) {
try {
is.close();
br.close();
} catch (IOException e) {
// ignore
}
}
}
return sb.toString();
}
/**
* Parses a JSON string and converts it to the correct Java object
* @param str The JSON string to parse
* @return
* @throws IOException
*/
private static Object parseJsonString(String str) throws IOException {
if (str == null)
return null;
str = str.trim();
char firstChar = str.charAt(0);
if (firstChar == '[' || firstChar == '{') {
// Array or Map
// Count the number of open/close arrays or objects
int numOpen = 0;
// Quote handling: Count the number of open single/double quotes, except when there is an
// open one already. This handles nested single quotes in double quotes, and vice versa.
int numSingleQuotes = 0;
int numDoubleQuotes = 0;
// Iterate through and split by pieces
int prevIndex = 1;
List<String> parts = new ArrayList<String>();
for (int i = 1; i < str.length() - 1; i++) {
char iChar = str.charAt(i);
if (iChar == '[' || iChar == '{')
numOpen++;
else if (iChar == ']' || iChar == '}')
numOpen--;
else if (iChar == '\'' && numDoubleQuotes % 2 == 0)
numSingleQuotes++;
else if (iChar == '"' && numSingleQuotes % 2 == 0)
numDoubleQuotes++;
// If split index, store the substring
if (numOpen == 0 && (numSingleQuotes % 2 == 0 && numDoubleQuotes % 2 == 0) && iChar == ',') {
parts.add(str.substring(prevIndex, i));
prevIndex = i + 1;
}
}
// Grab the last part if present
if (prevIndex < str.length() - 1) {
parts.add(str.substring(prevIndex, str.length() - 1));
}
// Decode the parts into the result
if (firstChar == '[') {
List ret = new ArrayList();
for (int arrayIndex = 0; arrayIndex < parts.size(); arrayIndex++)
ret.add(parseJsonString(parts.get(arrayIndex)));
return ret;
} else if (firstChar == '{') {
Map ret = new HashMap();
for (String part : parts) {
part = part.trim();
int colonIndex = part.indexOf(':');
String mapKey = part.substring(0, colonIndex);
mapKey = mapKey.substring(1, mapKey.length() - 1); // 1 to -1 to avoid the quotes
Object mapValue = parseJsonString(part.substring(colonIndex + 1, part.length()));
ret.put(mapKey, mapValue);
}
return ret;
}
return null;
} else if (firstChar == '"') // String
return str.substring(1, str.length() - 1);
else if ("true".equals(str))
return true;
else if ("false".equals(str))
return false;
else
return Double.parseDouble(str);
}
/**
* Converts a GeoJSON object to a list of LayerArea objects
* @param geoJSON The GeoJSON object containing this basemap layer's area geometry data
* @return
*/
private static List<LayerArea> createLayerAreas(Map geoJSON) {
List territories = Arrays.asList(MapProviderBean.territoryNames);
HashMap<String, DemoLayerArea> areaMap = new HashMap<String, DemoLayerArea>();
if (geoJSON != null) {
List features = (List) geoJSON.get("features");
int numFeatures = features.size();
for (int j = 0; j < numFeatures; j++) {
Map feature = (Map) features.get(j);
Map properties = (Map) feature.get("properties");
String label = (String) properties.get("POLYGON_NAME");
// We just want to render canada
if (!territories.contains(label))
continue;
Map geometry = (Map) feature.get("geometry");
Rectangle2D labelBox = null;
List<Double> labelBoxList = (List<Double>) feature.get("label_box");
if (labelBoxList != null) {
int minX = (int) (labelBoxList.get(0) / 2000);
int minY = (int) (labelBoxList.get(1) / 2000);
int maxX = (int) (labelBoxList.get(2) / 2000);
int maxY = (int) (labelBoxList.get(3) / 2000);
labelBox = new Rectangle(minX, -minY, maxX - minX, maxY - minY);
}
DemoLayerArea area = areaMap.get(label);
if (area != null)
area.setPath(area.getPath() + DemoMapProvider.simplifyGeometries(geometry));
else
areaMap.put(label,
new DemoLayerArea(label, null, label, labelBox, DemoMapProvider.simplifyGeometries(geometry),
null));
}
}
List<LayerArea> layerAreas = new ArrayList<LayerArea>();
for (Map.Entry<String, DemoLayerArea> entry : areaMap.entrySet())
layerAreas.add(entry.getValue());
return layerAreas;
}
/**
* Converts and simplifies area geometries to relative path commands
* @param geometry The map containing an area's coordinates
* @return
*/
private static String simplifyGeometries(Map geometry) {
StringBuilder sb = new StringBuilder();
String type = (String) geometry.get("type");
List coords = (List) geometry.get("coordinates");
int len = coords.size();
if ("Polygon".equals(type)) {
for (int i = 0; i < len; i++)
sb.append(DemoMapProvider.simplifyGeometriesHelper((List) coords.get(i)));
} else if ("MultiPolygon".equals(type)) {
for (int i = 0; i < len; i++) {
List nestCoords = (List) coords.get(i);
int nestCoordsLen = nestCoords.size();
for (int j = 0; j < nestCoordsLen; j++)
sb.append(DemoMapProvider.simplifyGeometriesHelper((List) coords.get(j)));
}
}
return sb.toString();
}
/**
* Helper method for parsing a GeoJSON geometry object
* @param coords The list of coordinates to simplify and convert to a relative path command
* @return
*/
private static String simplifyGeometriesHelper(List coords) {
List<Point2D> points = new ArrayList<Point2D>();
int len = coords.size();
// Convert coordinates to Point2D objects so we can use MapProviderUtils to simplify area gemoetries
// Also reduce data precision by dividing by 2000
for (int i = 0; i < len; i += 2)
points.add(new Point2D.Double(Math.floor((Double) coords.get(i) / 2000),
-Math.floor((Double) coords.get(i + 1) / 2000)));
return MapProviderUtils.convertToPath(points);
}
}
テーマ・マップ用のカスタム・ベース・マップを作成する場合は、oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerAreaを拡張し、テーマ・マップ・コンポーネントがdvt:areaLayerをレンダリングするために必要なデータを取得します。次に示すのは、カナダのカスタム・ベース・マップ例のためのlayerAreaクラスの実装です。
package oracle.adfdemo.view.feature.rich.thematicMap;
import java.awt.geom.Rectangle2D;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea;
public class DemoLayerArea extends LayerArea {
private String id;
private String shortLabel;
private String longLabel;
private Rectangle2D labelBox;
private String path;
private String parent;
public DemoLayerArea(String id, String shortLabel, String longLabel, Rectangle2D labelBox, String path, String parent) {
this.id = id;
this.shortLabel = shortLabel;
this.longLabel = longLabel;
this.labelBox = labelBox;
this.path = path;
this.parent = parent;
}
@Override
public String getId() {
return id;
}
@Override
public String getShortLabel() {
return shortLabel;
}
@Override
public String getLongLabel() {
return longLabel;
}
@Override
public Rectangle2D getLabelBox() {
return labelBox;
}
@Override
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String getParent() {
return parent;
}
}
次は、ツリーマップとサンバースト・コンポーネントを作成するサンプル・コードです。
ツリーマップあるいはサンバーストをUI優先開発を使用して作成するには、クラスとマネージドBeanを使用してツリー・ノードとツリー・モデルを定義し、ツリーにデータを移入し、かつツリーマップあるいはサンバースト構成の必要に応じ、追加のメソッドを追加します。
次の例に、国勢調査のデータ例でのツリー・ノードを定義するコードのサンプルを示します。ラベル、サイズ、および色についての必要な設定は、パラメータとしてツリー・ノードに渡されます。
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
public class TreeNode {
private final String m_text;
private final Number m_size;
private final Color m_color;
private final List<TreeNode> m_children = new ArrayList<TreeNode>();
public TreeNode(String text, Number size, Color color) {
m_text = text;
m_size = size;
m_color = color;
}
public String getText() {
return m_text;
}
public Number getSize() {
return m_size;
}
public Color getColor() {
return m_color;
}
public void addChild(TreeNode child) {
m_children.add(child);
}
public void addChildren(List<TreeNode> children) {
m_children.addAll(children);
}
public List<TreeNode> getChildren() {
return m_children;
}
@Override
public String toString() {
return m_text + ": " + m_color + " " + Math.round(m_size.doubleValue());
}
}
UI優先開発におけるツリーマップあるいはサンバーストにデータを供給するには、クラスあるいはマネージドBeanをユーザーのアプリケーションに追加します。アプリケーションは前述の例のツリー・ノードを展開し、そこにデータを移入します。ツリー・モデルを設定するクラスは、org.apache.myfaces.trinidad.model.TreeModelクラスの実装にする必要があります。ツリー・モデルを定義したら、org.apache.myfaces.trinidad.model.ChildPropertyTreeModelを実装するメソッドを作成してツリー・モデルを完成させます。
国勢データの例において、ルートと子ノードの構造を設定し、子レベルにデータを移入し、ノードの色およびサイズを定義するサンプル・クラスを次に示します。
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.apache.myfaces.trinidad.model.ChildPropertyTreeModel;
import org.apache.myfaces.trinidad.model.TreeModel;
public class CensusData {
public static TreeModel getUnitedStatesData() {
return getModel(ROOT);
}
public static TreeModel getRegionWestData() {
return getModel(REGION_W);
}
public static TreeModel getRegionNortheastData() {
return getModel(REGION_NE);
}
public static TreeModel getRegionMidwestData() {
return getModel(REGION_MW);
}
public static TreeModel getRegionSouthData() {
return getModel(REGION_S);
}
public static TreeModel getDivisionPacificData() {
return getModel(DIVISION_P);
}
private static TreeModel getModel(DataItem rootItem) {
TreeNode root = getTreeNode(rootItem);
return new ChildPropertyTreeModel(root, "children");
}
private static TreeNode getTreeNode(DataItem dataItem)
{
// Create the node itself
TreeNode node = new CensusTreeNode(dataItem.getName(),
dataItem.getPopulation(),
getColor(dataItem.getIncome(), MIN_INCOME, MAX_INCOME),
dataItem.getIncome());
// Create its children
List<TreeNode> children = new ArrayList<TreeNode>();
for(DataItem childItem : dataItem.children) {
children.add(getTreeNode(childItem));
}
// Add the children and return
node.addChildren(children);
return node;
}
private static Color getColor(double value, double min, double max) {
double percent = Math.max((value - min) / max, 0);
if(percent > 0.5) {
double modifier = (percent - 0.5) * 2;
return new Color((int)(modifier*102), (int)(modifier*153), (int)(modifier*51));
}
else {
double modifier = percent *2;
return new Color((int)(modifier*204), (int)(modifier*51), 0);
}
}
public static class DataItem {
private final String name;
private final int population;
private final int income;
private final List<DataItem> children;
public DataItem(String name, int population, int income) {
this.name = name;
this.population = population;
this.income = income;
this.children = new ArrayList<DataItem>();
}
public void addChild(DataItem child) {
this.children.add(child);
}
public String getName() {
return name;
}
public int getPopulation() {
return population;
}
public int getIncome() {
return income;
}
public List<CensusData.DataItem> getChildren() {
return children;
}
}
private static final int MIN_INCOME = 0;
private static final int MAX_INCOME = 70000;
private static final DataItem ROOT = new DataItem("United States", 301461533, 51425);
private static final DataItem REGION_NE = new DataItem("Northeast Region", 54906297, 57208);
private static final DataItem REGION_MW = new DataItem("Midwest Region", 66336038, 49932);
private static final DataItem REGION_S = new DataItem("South Region", 110450832, 47204);
private static final DataItem REGION_W = new DataItem("West Region", 69768366, 56171);
private static final DataItem DIVISION_NE = new DataItem("New England", 14315257, 61511);
private static final DataItem DIVISION_MA = new DataItem("Middle Atlantic", 40591040, 55726);
private static final DataItem DIVISION_ENC = new DataItem("East North Central", 46277998, 50156);
private static final DataItem DIVISION_WNC = new DataItem("West North Central", 20058040, 49443);
private static final DataItem DIVISION_SA = new DataItem("South Atlantic", 57805475, 50188);
private static final DataItem DIVISION_ESC = new DataItem("East South Central", 17966553, 41130);
private static final DataItem DIVISION_WSC = new DataItem("West South Central", 34678804, 45608);
private static final DataItem DIVISION_M = new DataItem("Mountain", 21303294, 51504);
private static final DataItem DIVISION_P = new DataItem("Pacific", 48465072, 58735);
static {
// Set up the regions
ROOT.addChild(REGION_NE);
ROOT.addChild(REGION_MW);
ROOT.addChild(REGION_S);
ROOT.addChild(REGION_W);
// Set up the divisions
REGION_NE.addChild(DIVISION_NE);
REGION_NE.addChild(DIVISION_MA);
REGION_MW.addChild(DIVISION_ENC);
REGION_MW.addChild(DIVISION_WNC);
REGION_S.addChild(DIVISION_SA);
REGION_S.addChild(DIVISION_ESC);
REGION_S.addChild(DIVISION_WSC);
REGION_W.addChild(DIVISION_M);
REGION_W.addChild(DIVISION_P);
// Set up the states
DIVISION_NE.addChild(new DataItem("Connecticut", 3494487, 67721));
DIVISION_NE.addChild(new DataItem("Maine", 1316380, 46541));
DIVISION_NE.addChild(new DataItem("Massachusetts", 6511176, 64496));
DIVISION_NE.addChild(new DataItem("New Hampshire", 1315419, 63033));
DIVISION_NE.addChild(new DataItem("Rhode Island", 1057381, 55569));
DIVISION_NE.addChild(new DataItem("Vermont", 620414, 51284));
DIVISION_MA.addChild(new DataItem("New Jersey", 8650548, 68981));
DIVISION_MA.addChild(new DataItem("New York", 19423896, 55233));
DIVISION_MA.addChild(new DataItem("Pennsylvania", 12516596, 49737));
DIVISION_ENC.addChild(new DataItem("Indiana", 6342469, 47465));
DIVISION_ENC.addChild(new DataItem("Illinois", 12785043, 55222));
DIVISION_ENC.addChild(new DataItem("Michigan", 10039208, 48700));
DIVISION_ENC.addChild(new DataItem("Ohio", 11511858, 47144));
DIVISION_ENC.addChild(new DataItem("Wisconsin", 5599420, 51569));
DIVISION_WNC.addChild(new DataItem("Iowa", 2978880, 48052));
DIVISION_WNC.addChild(new DataItem("Kansas", 2777835, 48394));
DIVISION_WNC.addChild(new DataItem("Minnesota", 5188581, 57007));
DIVISION_WNC.addChild(new DataItem("Missouri", 5904382, 46005));
DIVISION_WNC.addChild(new DataItem("Nebraska", 1772124, 47995));
DIVISION_WNC.addChild(new DataItem("North Dakota", 639725, 45140));
DIVISION_WNC.addChild(new DataItem("South Dakota", 796513, 44828));
DIVISION_SA.addChild(new DataItem("Delaware", 863832, 57618));
DIVISION_SA.addChild(new DataItem("District of Columbia", 588433, 56519));
DIVISION_SA.addChild(new DataItem("Florida", 18222420, 47450));
DIVISION_SA.addChild(new DataItem("Georgia", 9497667, 49466));
DIVISION_SA.addChild(new DataItem("Maryland", 5637418, 69475));
DIVISION_SA.addChild(new DataItem("North Carolina", 9045705, 45069));
DIVISION_SA.addChild(new DataItem("South Carolina", 4416867, 43572));
DIVISION_SA.addChild(new DataItem("Virginia", 7721730, 60316));
DIVISION_SA.addChild(new DataItem("West Virginia", 1811403, 37356));
DIVISION_ESC.addChild(new DataItem("Alabama", 4633360, 41216));
DIVISION_ESC.addChild(new DataItem("Kentucky", 4252000, 41197));
DIVISION_ESC.addChild(new DataItem("Mississippi", 2922240, 36796));
DIVISION_ESC.addChild(new DataItem("Tennessee", 6158953, 42943));
DIVISION_WSC.addChild(new DataItem("Arkansas", 2838143, 38542));
DIVISION_WSC.addChild(new DataItem("Louisiana", 4411546, 42167));
DIVISION_WSC.addChild(new DataItem("Oklahoma", 3610073, 41861));
DIVISION_WSC.addChild(new DataItem("Texas", 23819042, 48199));
DIVISION_M.addChild(new DataItem("Arizona", 6324865, 50296));
DIVISION_M.addChild(new DataItem("Colorado", 4843211, 56222));
DIVISION_M.addChild(new DataItem("Idaho", 1492573, 46183));
DIVISION_M.addChild(new DataItem("Montana", 956257, 43089));
DIVISION_M.addChild(new DataItem("Nevada", 2545763, 55585));
DIVISION_M.addChild(new DataItem("New Mexico", 1964860, 42742));
DIVISION_M.addChild(new DataItem("Utah", 2651816, 55642));
DIVISION_M.addChild(new DataItem("Wyoming", 523949, 51990));
DIVISION_P.addChild(new DataItem("Alaska", 683142, 64635));
DIVISION_P.addChild(new DataItem("California", 36308527, 60392));
DIVISION_P.addChild(new DataItem("Hawaii", 1280241, 64661));
DIVISION_P.addChild(new DataItem("Oregon", 3727407, 49033));
DIVISION_P.addChild(new DataItem("Washington", 6465755, 56384));
}
public static class CensusTreeNode extends TreeNode {
private int income;
public CensusTreeNode(String text, Number size, Color color, int income) {
super(text, size, color);
this.income = income;
}
public int getIncome() {
return income;
}
}
}
最後に、UI-first開発におけるツリー・モデルを完成するには、マネージドBeanを、データを含むクラスまたはBeanを参照するユーザーのアプリケーションに加え、オプションとして、ツリーマップあるいはサンバーストのカスタマイズのために他のメソッドを追加します。
国勢調査のツリーマップをインスタンス化し、それに国勢調査データを移入するコードのサンプルを次に示します。この例はまた、ツリーマップのノードの行データをラベル表示のための文字列に変換するサンプル・メソッド(convertToString)を含んでいます。
import org.apache.myfaces.trinidad.component.UIXHierarchy;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;
import oracle.adf.view.faces.bi.component.treemap.UITreemap;
public class SampleTreemap {
// Data Model Attrs
private TreeModel currentModel;
private final CensusData censusData = new CensusData();
private String censusRoot = "United States";
private UITreemap treemap;
public TreeModel getCensusRootData() {
return censusData.getUnitedStatesData();
}
public TreeModel getCensusData() {
if ("West Region".equals(censusRoot))
return censusData.getRegionWestData();
else if ("South Region".equals(censusRoot))
return censusData.getRegionSouthData();
else if ("Midwest Region".equals(censusRoot))
return censusData.getRegionMidwestData();
else if ("Northeast Region".equals(censusRoot))
return censusData.getRegionNortheastData();
else if ("Pacific Division".equals(censusRoot))
return censusData.getDivisionPacificData();
else
return censusData.getUnitedStatesData();
}
public TreeModel getData() {
// Return cached data model if available
if(currentModel != null)
return currentModel;
currentModel = getCensusData();
return currentModel;
}
public void setCensusRoot(String censusRoot) {
this.censusRoot = censusRoot;
}
public String getCensusRoot() {
return censusRoot;
}
//Converts the rowKeySet into a string of node text labels.
public static String convertToString(RowKeySet rowKeySet, UIXHierarchy hierarchy) {
StringBuilder s = new StringBuilder();
if (rowKeySet != null) {
for (Object rowKey : rowKeySet) {
TreeNode rowData = (TreeNode)hierarchy.getRowData(rowKey);
s.append(rowData.getText()).append(", ");
}
// Remove the trailing comma
if (s.length() > 0)
s.setLength(s.length() - 2);
}
return s.toString();
}
public void setTreemap(UITreemap treemap) {
this.treemap = treemap;
}
public UITreemap getTreemap() {
return treemap;
}
}
両方のコンポーネントは同じツリー・モデルを使用するため、サンバースト国勢調査の例を設定するコードはほとんど同一です。例は、G.2.2項「サンバースト・マネージドBeanのサンプル・コード」を参照してください。
次のサンプル・コードは、国勢調査サンバーストをインスタンス化し、それに国勢調査データを移入します。この例はまた、サンバーストのノードの行データをラベル表示のための文字列に変換するサンプル・メソッド(convertToString)を含んでいます。
import oracle.adf.view.faces.bi.component.sunburst.UISunburst;
import org.apache.myfaces.trinidad.component.UIXHierarchy;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;
public class SunburstSample {
// Components
private UISunburst sunburst;
// Attributes
private TreeModel currentModel;
private final CensusData censusData = new CensusData();
private String censusRoot = "United States";
public TreeModel getCensusRootData() {
return censusData.getUnitedStatesData();
}
public TreeModel getCensusData() {
if ("West Region".equals(censusRoot))
return censusData.getRegionWestData();
else if ("South Region".equals(censusRoot))
return censusData.getRegionSouthData();
else if ("Midwest Region".equals(censusRoot))
return censusData.getRegionMidwestData();
else if ("Northeast Region".equals(censusRoot))
return censusData.getRegionNortheastData();
else if ("Pacific Division".equals(censusRoot))
return censusData.getDivisionPacificData();
else
return censusData.getUnitedStatesData();
}
public TreeModel getData() {
// Return cached data model if available
if(currentModel != null)
return currentModel;
currentModel = getCensusData();
return currentModel;
}
public void setCensusRoot(String censusRoot) {
this.censusRoot = censusRoot;
}
public String getCensusRoot() {
return censusRoot;
}
public static String convertToString(RowKeySet rowKeySet, UIXHierarchy hierarchy) {
StringBuilder s = new StringBuilder();
if (rowKeySet != null) {
for (Object rowKey : rowKeySet) {
TreeNode rowData = (TreeNode)hierarchy.getRowData(rowKey);
s.append(rowData.getText()).append(", ");
}
// Remove the trailing comma
if (s.length() > 0)
s.setLength(s.length() - 2);
}
return s.toString();
}
public void setSunburst(UISunburst sunburst) {
this.sunburst = sunburst;
}
public UISunburst getSunburst() {
return sunburst;
}
}
次に示すのは、DVTダイアグラムのレイアウト・フレームワークを使用して、ダイアグラム・コンポーネントを作成するコード例です。
UI優先開発を使用してダイアグラムを作成する場合、JDeveloperにより、力指向グラフ描画アルゴリズムに基づくデフォルト・クライアント・レイアウト・オプションが提供され、それを使用したり、編集できます。
var Application1DiagramLayout = function(layoutContext, optLinkLength, initialTemp) {
this._layoutContext = layoutContext;
this._optLinkLength = optLinkLength;
this._initialTemp = initialTemp;
};
//pad factor for the node size
Application1DiagramLayout.PAD_FACTOR = 1.2;
//initial temperature factor - percent of ideal viewport dimension
Application1DiagramLayout.INIT_TEMP_FACTOR = .25;
//number of iterations to run
Application1DiagramLayout.ITERATIONS = 200;
/**
* Main function that does the force directed layout (Layout entry point)
* See algorithm in "Graph Drawing by Force-directed Placement" by Thomas M. J. Fruchterman and Edward M. Reingold
* @param {DvtDiagramLayoutContext} layoutContext object that defines a context for layout call
*/
Application1DiagramLayout.forceDirectedLayout = function(layoutContext)
{
//pretend that the layout area is just big enough to fit all the nodes
var maxBounds = Application1DiagramLayout.getMaxNodeBounds(layoutContext);
var nodeCount = layoutContext.getNodeCount();
var area = nodeCount * (Application1DiagramLayout.PAD_FACTOR * maxBounds.w) * (Application1DiagramLayout.PAD_FACTOR * maxBounds.h);
var initialTemp = Application1DiagramLayout.INIT_TEMP_FACTOR * Math.sqrt(area);
//optimal link length - default is just the size of an ideal grid cell
var layoutAttrs = layoutContext.getLayoutAttributes();
var optLinkLength = (layoutAttrs && layoutAttrs["optimalLinkLength"]) ? parseFloat(layoutAttrs["optimalLinkLength"]) : Math.sqrt(area / nodeCount);
//initialize and run the layout
var layout = new Application1DiagramLayout(layoutContext, optLinkLength, initialTemp);
layout.layoutNodes();
layout.layoutLinks();
//position labels
layout.positionNodeLabels();
layout.positionLinkLabels();
};
/**
* Layout nodes
*/
Application1DiagramLayout.prototype.layoutNodes = function () {
this.initForceDirectedPositions();
var iter = Application1DiagramLayout.ITERATIONS;
var temp = this._initialTemp;
for (var i = 0; i < iter; i++) {
this.runForceDirectedIteration(temp);
//after each iteration, decrease the temperature - we do it linearly
temp = this._initialTemp * (1 - (i + 1)/iter);
}
};
/**
* Reposition nodes using force directed algorithm for a single iteration
* @param {number} t temperature for the iteration that used to limit node displacement
*/
Application1DiagramLayout.prototype.runForceDirectedIteration = function(t)
{
//calculate the repulsive force between each two nodes
var nodeCount = this._layoutContext.getNodeCount();
for (var ni = 0; ni < nodeCount; ni++) {
var node = this._layoutContext.getNodeByIndex(ni);
node.disp = new DvtDiagramPoint(0, 0);
for (var ni2 = 0; ni2 < nodeCount; ni2++) {
if (ni == ni2)
continue;
var node2 = this._layoutContext.getNodeByIndex(ni2);
var difference = this._subtractVectors(node.getPosition(), node2.getPosition());
var distance = this._vectorLength(difference);
var repulsion = (this._optLinkLength * this._optLinkLength) / distance;
node.disp = this._addVectors(node.disp, this._scaleVector(difference, repulsion / distance ));
}
}
//calculate the attractive force between linked nodes
var linkCount = this._layoutContext.getLinkCount();
for (var li = 0; li < linkCount; li++) {
var link = this._layoutContext.getLinkByIndex(li);
var node = this._getNodeAtCurrentLevel (link.getStartId());
var node2 = this._getNodeAtCurrentLevel (link.getEndId());
if (!node || !node2)
continue;
var difference = this._subtractVectors(node.getPosition(), node2.getPosition());
var distance = this._vectorLength(difference);
var attraction = (distance * distance) / this._optLinkLength;
node.disp = this._subtractVectors(node.disp, this._scaleVector(difference, attraction / distance ) );
node2.disp = this._addVectors(node2.disp, this._scaleVector(difference, attraction / distance ) );
}
//limit node displacement by the temperature t and set the position
for (var ni = 0; ni < nodeCount; ni++) {
var node = this._layoutContext.getNodeByIndex(ni);
this._addGravity(node);
var distance = this._vectorLength(node.disp);
var pos = this._addVectors(node.getPosition(), this._scaleVector(node.disp, Math.min(distance, t) / distance));
node.setPosition(pos);
}
};
/**
* Adds gravity force that attracts a node to the center, the gravity force does not allow disconnected nodes and branches to be pushed far away from the center
* @param {DvtDiagramLayoutContextNode} node object that defines node context for the layout
*/
Application1DiagramLayout.prototype._addGravity = function(node) {
var gravityAdjustment = .2;
var distance = this._vectorLength(node.getPosition()); //distance from the center (0,0)
var attraction = (distance * distance) / this._optLinkLength;
node.disp = this._subtractVectors(node.disp, this._scaleVector(node.getPosition(), attraction / distance * gravityAdjustment ) );
};
/**
* Initializes node positions - node positions in force directed layout must be initialized such that no
* two nodes have the same position. Position nodes in a circle.
*/
Application1DiagramLayout.prototype.initForceDirectedPositions = function() {
var nodeCount = this._layoutContext.getNodeCount();
var angleStep = 2*Math.PI / nodeCount;
var radius = this._optLinkLength;
for (var ni = 0; ni < nodeCount; ni++) {
var x = radius * Math.cos(angleStep * ni);
var y = radius * Math.sin(angleStep * ni);
var node = this._layoutContext.getNodeByIndex(ni);
node.setPosition(new DvtDiagramPoint(x, y));
}
};
/**
* Calculate vector length
* @param {DvtDiagramPoint} p vector
* @return {number} vector length
*/
Application1DiagramLayout.prototype._vectorLength = function(p) {
return Math.sqrt(p.x * p.x + p.y * p.y);
};
/**
* Scale vector
* @param {DvtDiagramPoint} p vector
* @param {number} scale scale
* @return {DvtDiagramPoint} resulting vector
*/
Application1DiagramLayout.prototype._scaleVector = function(p, scale) {
return new DvtDiagramPoint(p.x * scale, p.y * scale);
};
/**
* Adds vectors
* @param {DvtDiagramPoint} p1 vector
* @param {DvtDiagramPoint} p2 vector
* @return {DvtDiagramPoint} resulting vector
*/
Application1DiagramLayout.prototype._addVectors = function(p1, p2) {
return new DvtDiagramPoint(p1.x + p2.x, p1.y + p2.y);
};
/**
* Subtract vectors
* @param {DvtDiagramPoint} p1 vector
* @param {DvtDiagramPoint} p2 vector
* @return {DvtDiagramPoint} resulting vector
*/
Application1DiagramLayout.prototype._subtractVectors = function(p1, p2) {
return new DvtDiagramPoint(p1.x - p2.x, p1.y - p2.y);
};
/**
* Finds a node for the link by the node id. In case of a link that does not connect nodes across containers, that will be a node itself.
* In case when a link connects nodes across containers, that might be one of the ancestor nodes - the node that has been processed at the current level.
* @param {string} nodeId id of the node to check
*/
Application1DiagramLayout.prototype._getNodeAtCurrentLevel = function(nodeId) {
var node;
do {
if (!nodeId)
return null;
node = this._layoutContext.getNodeById(nodeId);
nodeId = node.getContainerId();
} while (!node.disp);
return node;
};
/**
* Create links
*/
Application1DiagramLayout.prototype.layoutLinks = function () {
for (var li = 0;li < this._layoutContext.getLinkCount();li++) {
var link = this._layoutContext.getLinkByIndex(li);
link.setPoints(this.getEndpoints(link));
}
};
/**
* Get endpoints for the link
* @param {DvtDiagramLayoutContextLink} link object that defines link context for the layout
* @return {array} an array that contains the start X, Y coordinates and the end X, Y coordinates for the link
*/
Application1DiagramLayout.prototype.getEndpoints = function (link) {
var n1 = this._layoutContext.getNodeById(link.getStartId());
var n2 = this._layoutContext.getNodeById(link.getEndId());
var n1Position = n1.getPosition();
var n2Position = n2.getPosition();
if (n1.getContainerId() || n2.getContainerId()) { //for cross-container link
n1Position = this._layoutContext.localToGlobal(new DvtDiagramPoint(0, 0), n1);
n2Position = this._layoutContext.localToGlobal(new DvtDiagramPoint(0, 0), n2);
}
var b1 = n1.getContentBounds();
var b2 = n2.getContentBounds();
var startX = n1Position.x + b1.x + .5 * b1.w;
var startY = n1Position.y + b1.y + .5 * b1.h;
var endX = n2Position.x + b2.x + .5 * b2.w;
var endY = n2Position.y + b2.y + .5 * b2.h;
b1 = new DvtDiagramRectangle(n1Position.x + b1.x, n1Position.y + b1.y, b1.w, b1.h);
b2 = new DvtDiagramRectangle(n2Position.x + b2.x, n2Position.y + b2.y, b2.w, b2.h);
var startP = this._findLinkNodeIntersection(b1, startX, startY, endX, endY, link.getStartConnectorOffset());
var endP = this._findLinkNodeIntersection(b2, endX, endY, startX, startY, link.getEndConnectorOffset());
return [startP.x, startP.y, endP.x, endP.y];
};
/**
* Find a point where a link line intersects the node boundary - use that point as the start or the end connection point
* @param {DvtDiagramRectangle} rect the bounds of the node content
* @param {number} startX x coordinate for the line start
* @param {number} startY y coordinate for the line start
* @param {number} endX x coordinate for the line end
* @param {number} endY y coordinate for the line end
* @param {number} connOffset the offset of the start connector
* @return {DvtDiagramPoint} a point where a link line intersects the node boundary
*/
Application1DiagramLayout.prototype._findLinkNodeIntersection = function (rect, startX, startY, endX, endY, connOffset) {
var lineAngle = Math.atan2(endY - startY, endX - startX);
var cornerAngle = Math.atan2(rect.h, rect.w); // rectangle diagonal from top left to right bottom
var bottomRightAngle = cornerAngle;
var bottomLeftAngle = Math.PI - bottomRightAngle;
var topRightAngle = - bottomRightAngle;
var topLeftAngle = - bottomLeftAngle;
var x = 0, y = 0;
if (lineAngle >= topLeftAngle && lineAngle <= topRightAngle) { // side top
x = rect.x + rect.w * .5 + Math.tan(Math.PI / 2 - lineAngle) * (-rect.h * .5);
y = rect.y;
}
else if (lineAngle <= bottomLeftAngle && lineAngle >= bottomRightAngle) { // side bottom
x = rect.x + rect.w * .5 + Math.tan(Math.PI / 2 - lineAngle) * (rect.h * .5);
y = rect.y + rect.h;
}
else if (lineAngle <= bottomRightAngle && lineAngle >= topRightAngle) { // side right
x = rect.x + rect.w;
y = rect.y + rect.h * .5 + Math.tan(lineAngle) * (rect.w * .5);
}
else { //side left
x = rect.x;
y = rect.y + rect.h * .5 + Math.tan(lineAngle) * (- rect.w * .5);
}
if (connOffset) {
x += Math.cos(lineAngle) * connOffset;
y += Math.sin(lineAngle) * connOffset;
}
return new DvtDiagramPoint(x, y);
};
/**
* Center node label below the node
*/
Application1DiagramLayout.prototype.positionNodeLabels = function () {
for (var ni = 0; ni < this._layoutContext.getNodeCount();ni++) {
var node = this._layoutContext.getNodeByIndex(ni);
var nodeLabelBounds = node.getLabelBounds();
if (nodeLabelBounds) {
var nodeBounds = node.getContentBounds();
var nodePos = node.getPosition();
var labelX = nodeBounds.x + nodePos.x + .5 * (nodeBounds.w - nodeLabelBounds.w);
var labelY = nodeBounds.y + nodePos.y + nodeBounds.h;
node.setLabelPosition(new DvtDiagramPoint(labelX, labelY));
}
}
};
/**
* Position link label at the link center
*/
Application1DiagramLayout.prototype.positionLinkLabels = function (layoutContext) {
for (var ni = 0;ni < this._layoutContext.getLinkCount();ni++) {
var link = this._layoutContext.getLinkByIndex(ni);
var linkLabelBounds = link.getLabelBounds();
if (linkLabelBounds) {
var points = link.getPoints();
if (points.length >=4) {
var startX = points[0], endX = points[points.length-2];
var startY = points[1], endY = points[points.length-1];
var labelX = startX + .5 * (endX - startX - linkLabelBounds.w);
var labelY = startY + .5 * (endY - startY - linkLabelBounds.h);
link.setLabelPosition(new DvtDiagramPoint(labelX, labelY));
}
}
}
};
/**
* Helper function that finds max node width and height
* @param {DvtDiagramLayoutContext} layoutContext Object that defines a context for layout call
* @return {DvtDiagramRectangle} a rectangle that represent a node with max width and max height
*/
Application1DiagramLayout.getMaxNodeBounds = function (layoutContext) {
var nodeCount = layoutContext.getNodeCount();
var maxW = 0 , maxH = 0;
for (var ni = 0;ni < nodeCount;ni++) {
var node = layoutContext.getNodeByIndex(ni);
var bounds = node.getContentBounds();
maxW = Math.max(bounds.w, maxW);
maxH = Math.max(bounds.h, maxH);
}
return new DvtDiagramRectangle(0, 0, maxW, maxH);
};
ノードおよびリンクの単純な円形レイアウト用です。
var LearningDiagramLayouts = {
simpleVertical : function (layoutContext) {
var largestNodeBounds = LearningDiagramLayouts.getMaxNodeBounds(layoutContext);
var nodeGap = (2 * largestNodeBounds.h);
var nodeCount = layoutContext.getNodeCount();
var linkCount = layoutContext.getLinkCount();
var xPos = 0;
var yPos = 0;
var labelXPos = largestNodeBounds.w + 10;
for (var i = 0;i < nodeCount;i++) {
var node = layoutContext.getNodeByIndex(i);
var nodeHeight = node.getContentBounds().h;
node.setPosition(new DvtDiagramPoint(xPos, yPos));
var labelHeight = node.getLabelBounds().h;
var labelYPos = yPos + ((nodeHeight - labelHeight) / 2);
node.setLabelPosition(new DvtDiagramPoint(labelXPos, labelYPos));
yPos += (nodeHeight + nodeGap);
}
var linkGap = 4;
var largestLinkLabelBounds = LearningDiagramLayouts.getMaxLinkLabelBounds(layoutContext);
for (var j = 0;j < linkCount;j++) {
var link = layoutContext.getLinkByIndex(j);
var startNode = layoutContext.getNodeById(link.getStartId());
var endNode = layoutContext.getNodeById(link.getEndId());
var linkStartPos = LearningDiagramLayouts.getNodeEdgeMidPoint(startNode, "s");
var linkEndPos = LearningDiagramLayouts.getNodeEdgeMidPoint(endNode, "n");
var linkLabelXPos;
var linkLabelYPos = linkStartPos.y + ((linkEndPos.y - linkStartPos.y - link.getLabelBounds().h) / 2);
if (linkEndPos.y > linkStartPos.y) {
link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), linkEndPos.x, (linkEndPos.y - linkGap)]);
linkLabelXPos = linkStartPos.x - (link.getLabelBounds().w + 20);
} else {
// need to re-route in this case
// Segment 1 - keep heading down for a short distance
var turn1 = new DvtDiagramPoint();
turn1.x = linkStartPos.x;
turn1.y = linkStartPos.y + (largestNodeBounds.h/2);
// Segment 2 - head left far enough to avoid overlap with the other link labels
var turn2 = new DvtDiagramPoint();
turn2.x = turn1.x - (largestLinkLabelBounds.w + 40);
turn2.y = turn1.y;
// Segment 3 - Back up the diagram to a point equally above the end node
var turn3 = new DvtDiagramPoint();
turn3.x = turn2.x;
turn3.y = linkEndPos.y - (largestNodeBounds.h/2);
// Segment 4 - Back to the center line
var turn4 = new DvtDiagramPoint();
turn4.x = linkEndPos.x;
turn4.y = turn3.y;
// Segment 5 - And down to the end node for which
// we already have the coordinates
//Now pass in the path:
link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap),
turn1.x, turn1.y,
turn2.x, turn2.y,
turn3.x, turn3.y,
turn4.x, turn4.y,
linkEndPos.x, (linkEndPos.y - linkGap)]);
//Finally work out the X position of the label in this case
linkLabelXPos = turn3.x - (link.getLabelBounds().w + 20);
}
link.setLabelPosition(new DvtDiagramPoint(linkLabelXPos, linkLabelYPos));
}
},
/**
* Utility function to return the size information for
* the largest node being handled by the layout
*/
getMaxNodeBounds : function (layoutContext) {
var nodeCount = layoutContext.getNodeCount();
var maxW = 0;
var maxH = 0;
for (var i = 0;i < nodeCount;i++) {
var node = layoutContext.getNodeByIndex(i);
var bounds = node.getContentBounds();
maxW = Math.max(bounds.w, maxW);
maxH = Math.max(bounds.h, maxH);
}
return new DvtDiagramRectangle(0, 0, maxW, maxH);
},
/**
* Utility function to return the size information for
* the largest link label being handled by the layout
*/
getMaxLinkLabelBounds : function (layoutContext) {
var linkCount = layoutContext.getLinkCount();
var maxW = 0;
var maxH = 0;
for (var i = 0;i < linkCount;i++) {
var link = layoutContext.getLinkByIndex(i);
var bounds = link.getLabelBounds();
maxW = Math.max(bounds.w, maxW);
maxH = Math.max(bounds.h, maxH);
}
return new DvtDiagramRectangle(0, 0, maxW, maxH);
},
/**
* Utility function to return the midpoint of a node edge.
* Edges are identified as compass points n e s w
*/
getNodeEdgeMidPoint : function (node, edge) {
var nodeSize = node.getContentBounds();
var nodePosition = node.getPosition();
var xpos;
var ypos;
switch (edge) {
case "n":
case "s":
xpos = nodePosition.x + (nodeSize.w / 2);
break;
case "e":
xpos = nodePosition.x + nodeSize.w;
break;
case "w":
xpos = nodePosition.x;
break;
default :
xpos = 0;
}
switch (edge) {
case "e":
case "w":
ypos = nodePosition.y + (nodeSize.h / 2);
break;
case "s":
ypos = nodePosition.y + nodeSize.h;
break;
case "n":
ypos = nodePosition.y;
break;
default :
ypos = 0;
}
return new DvtDiagramPoint(xpos, ypos);
}
}
このダイアグラム・レイアウトは、1ノード当たり1つの受信リンクと1つの送信リンクを超えないことを仮定し、制限付きの垂直レイアウトを使用しています。
var LearningDiagramLayouts = {
simpleVertical : function (layoutContext) {
var largestNodeBounds = LearningDiagramLayouts.getMaxNodeBounds(layoutContext);
var nodeGap = (2 * largestNodeBounds.h);
var nodeCount = layoutContext.getNodeCount();
var linkCount = layoutContext.getLinkCount();
var xPos = 0;
var yPos = 0;
var labelXPos = largestNodeBounds.w + 10;
for (var i = 0;i < nodeCount;i++) {
var node = layoutContext.getNodeByIndex(i);
var nodeHeight = node.getContentBounds().h;
node.setPosition(new DvtDiagramPoint(xPos, yPos));
var labelHeight = node.getLabelBounds().h;
var labelYPos = yPos + ((nodeHeight - labelHeight) / 2);
node.setLabelPosition(new DvtDiagramPoint(labelXPos, labelYPos));
yPos += (nodeHeight + nodeGap);
}
var linkGap = 4;
var largestLinkLabelBounds = LearningDiagramLayouts.getMaxLinkLabelBounds(layoutContext);
for (var j = 0;j < linkCount;j++) {
var link = layoutContext.getLinkByIndex(j);
var startNode = layoutContext.getNodeById(link.getStartId());
var endNode = layoutContext.getNodeById(link.getEndId());
var linkStartPos = LearningDiagramLayouts.getNodeEdgeMidPoint(startNode, "s");
var linkEndPos = LearningDiagramLayouts.getNodeEdgeMidPoint(endNode, "n");
var linkLabelXPos;
var linkLabelYPos = linkStartPos.y + ((linkEndPos.y - linkStartPos.y - link.getLabelBounds().h) / 2);
if (linkEndPos.y > linkStartPos.y) {
link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), linkEndPos.x, (linkEndPos.y - linkGap)]);
linkLabelXPos = linkStartPos.x - (link.getLabelBounds().w + 20);
} else {
// need to re-route in this case
// Segment 1 - keep heading down for a short distance
var turn1 = new DvtDiagramPoint();
turn1.x = linkStartPos.x;
turn1.y = linkStartPos.y + (largestNodeBounds.h/2);
// Segment 2 - head left far enough to avoid overlap with the other link labels
var turn2 = new DvtDiagramPoint();
turn2.x = turn1.x - (largestLinkLabelBounds.w + 40);
turn2.y = turn1.y;
// Segment 3 - Back up the diagram to a point equally above the end node
var turn3 = new DvtDiagramPoint();
turn3.x = turn2.x;
turn3.y = linkEndPos.y - (largestNodeBounds.h/2);
// Segment 4 - Back to the center line
var turn4 = new DvtDiagramPoint();
turn4.x = linkEndPos.x;
turn4.y = turn3.y;
// Segment 5 - And down to the end node for which
// we already have the coordinates
//Now pass in the path:
link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap),
turn1.x, turn1.y,
turn2.x, turn2.y,
turn3.x, turn3.y,
turn4.x, turn4.y,
linkEndPos.x, (linkEndPos.y - linkGap)]);
//Finally work out the X position of the label in this case
linkLabelXPos = turn3.x - (link.getLabelBounds().w + 20);
}
link.setLabelPosition(new DvtDiagramPoint(linkLabelXPos, linkLabelYPos));
}
},
/**
* Utility function to return the size information for
* the largest node being handled by the layout
*/
getMaxNodeBounds : function (layoutContext) {
var nodeCount = layoutContext.getNodeCount();
var maxW = 0;
var maxH = 0;
for (var i = 0;i < nodeCount;i++) {
var node = layoutContext.getNodeByIndex(i);
var bounds = node.getContentBounds();
maxW = Math.max(bounds.w, maxW);
maxH = Math.max(bounds.h, maxH);
}
return new DvtDiagramRectangle(0, 0, maxW, maxH);
},
/**
* Utility function to return the size information for
* the largest link label being handled by the layout
*/
getMaxLinkLabelBounds : function (layoutContext) {
var linkCount = layoutContext.getLinkCount();
var maxW = 0;
var maxH = 0;
for (var i = 0;i < linkCount;i++) {
var link = layoutContext.getLinkByIndex(i);
var bounds = link.getLabelBounds();
maxW = Math.max(bounds.w, maxW);
maxH = Math.max(bounds.h, maxH);
}
return new DvtDiagramRectangle(0, 0, maxW, maxH);
},
/**
* Utility function to return the midpoint of a node edge.
* Edges are identified as compass points n e s w
*/
getNodeEdgeMidPoint : function (node, edge) {
var nodeSize = node.getContentBounds();
var nodePosition = node.getPosition();
var xpos;
var ypos;
switch (edge) {
case "n":
case "s":
xpos = nodePosition.x + (nodeSize.w / 2);
break;
case "e":
xpos = nodePosition.x + nodeSize.w;
break;
case "w":
xpos = nodePosition.x;
break;
default :
xpos = 0;
}
switch (edge) {
case "e":
case "w":
ypos = nodePosition.y + (nodeSize.h / 2);
break;
case "s":
ypos = nodePosition.y + nodeSize.h;
break;
case "n":
ypos = nodePosition.y;
break;
default :
ypos = 0;
}
return new DvtDiagramPoint(xpos, ypos);
}
}