Skip to content

Commit fa5a480

Browse files
authored
Added ability to display metadata of a selected feature in a small inline dialog. (#13)
Additional enhancements: * Added bubble on node that shows # of links * Removed dependency on image file for drawing arrows * Added group-color setting so consumer can define node colors based on feature group property
1 parent 0082dc9 commit fa5a480

13 files changed

+396
-65
lines changed

dist/images/arrow-black.png

-673 Bytes
Binary file not shown.

dist/ml-ol-maps-ng-tpls.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ try {
66
}
77
module.run(['$templateCache', function($templateCache) {
88
$templateCache.put('/templates/detail-map.html',
9-
'<div id="detailMap"><div class="detailMapTools" ng-show="enableLinks"><label><input type="checkbox" ng-model="ctrl.hideLinks" ng-change="ctrl.toggleHideLinks()"> Hide Links</label> <button type="button" class="btn btn-danger btn-sm pull-right" ng-click="ctrl.resetData()">Reset</button></div><div><openlayers id="olDetailMap" class="map-detail" ol-center="ctrl.mapSettings.center" ol-defaults="ctrl.mapSettings.defaults" custom-layers="true"><ol-control name="mouseposition" ol-control-properties="ctrl.mapSettings.mousePosition"></ol-control><ol-layer ol-layer-properties="ctrl.mapSettings.baseMap"></ol-layer><ol-layer ng-show="!ctrl.hideLinks" ol-layer-properties="ctrl.mapSettings.lineLayer"></ol-layer><ol-layer ol-layer-properties="ctrl.mapSettings.ptLayer"></ol-layer></openlayers></div></div>');
9+
'<div id="detailMap"><div class="detailMapTools" ng-show="enableLinks"><label><input type="checkbox" ng-model="ctrl.hideLinks" ng-change="ctrl.toggleHideLinks()"> Hide Links</label> <button type="button" class="btn btn-danger btn-sm pull-right" ng-click="ctrl.resetData()">Reset</button></div><div><div class="map-detail-metadata-container"><div class="panel panel-default map-detail-metadata" ng-show="ctrl.enablePropertiesView && ctrl.mapItemSelected"><div class="panel-heading">{{ ctrl.mapItemSelected.label }} <button type="button" class="fa fa-times pull-right close" ng-click="ctrl.closeMetadata()"></button></div><div class="panel-body"><ul class="list-group"><li class="list-group-item text-right" ng-repeat="(key, value) in ctrl.mapItemSelected"><span class="pull-left"><strong>{{key}}</strong></span> {{ value }}</li></ul></div></div></div><openlayers id="olDetailMap" class="map-detail" ol-center="ctrl.mapSettings.center" ol-defaults="ctrl.mapSettings.defaults" custom-layers="true"><ol-control name="mouseposition" ol-control-properties="ctrl.mapSettings.mousePosition"></ol-control><ol-layer ol-layer-properties="ctrl.mapSettings.baseMap"></ol-layer><ol-layer ng-show="!ctrl.hideLinks" ol-layer-properties="ctrl.mapSettings.lineLayer"></ol-layer><ol-layer ol-layer-properties="ctrl.mapSettings.ptLayer"></ol-layer></openlayers></div></div>');
1010
}]);
1111
})();
1212

dist/ml-ol-maps-ng-tpls.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ml-ol-maps-ng.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,19 @@
77
.detailMapTools {
88
margin-bottom: 10px;
99
}
10+
.map-detail-metadata-container {
11+
position: relative;
12+
height: 0;
13+
width: 100%;
14+
}
15+
.map-detail-metadata {
16+
background: #f1f2f2;
17+
position: absolute;
18+
right: 2px;
19+
width: 300px;
20+
top: 2px;
21+
padding: 2px;
22+
z-index: 100;
23+
border: 1px solid #ccc;
24+
font-size: 8pt;
25+
}

dist/ml-ol-maps-ng.js

Lines changed: 169 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
baseMap: '=',
2222
zoom: '=',
2323
geometry: '=',
24+
groups: '=',
2425
enableLinks: '=',
26+
enablePropertiesView: '=',
2527

2628
// parent callbacks
2729
parentSingleClick: '&singleClick',
@@ -36,14 +38,27 @@
3638
MLOlDetailMapController.$inject = ['$scope', 'mlOlHelper', 'mapLinksService'];
3739
function MLOlDetailMapController($scope, mlOlHelper, mapLinksService) {
3840
var ctrl = this;
41+
var i = 0;
3942
ctrl.pointMap = {};
4043
ctrl.geometries = [];
44+
ctrl.mapItemSelected = null;
4145

4246
ctrl.hideLinks = false;
4347
if ($scope.enableLinks !== undefined) {
4448
ctrl.hideLinks = !$scope.enableLinks;
4549
}
4650

51+
ctrl.enablePropertiesView = true;
52+
if ($scope.enablePropertiesView !== undefined) {
53+
ctrl.enablePropertiesView = $scope.enablePropertiesView;
54+
}
55+
56+
if ($scope.groups !== undefined) {
57+
for (i = 0; i < $scope.groups.length; i++) {
58+
mlOlHelper.setGroupColor($scope.groups[i].name, $scope.groups[i].color);
59+
}
60+
}
61+
4762
$scope.$watch('features', function(data) {
4863
ctrl.addMapNodes($scope.features);
4964
});
@@ -62,9 +77,9 @@
6277
$scope.parentSingleClick({ 'feature': feature });
6378
}
6479

65-
// if (feature.get('metadata')) {
66-
// ctrl.mapItemSelected = feature.get('metadata');
67-
// }
80+
if (feature.get('metadata')) {
81+
ctrl.mapItemSelected = feature.get('metadata');
82+
}
6883
});
6984
});
7085
}
@@ -96,6 +111,10 @@
96111
ctrl.mapSettings.lineLayer.visible = !ctrl.hideLinks;
97112
};
98113

114+
ctrl.closeMetadata = function() {
115+
ctrl.mapItemSelected = null;
116+
};
117+
99118
ctrl.addLinkedNodes = function(results) {
100119
var tmpPoints = [];
101120
var i = 0;
@@ -107,7 +126,10 @@
107126
properties: {
108127
name: results.nodes[i].label,
109128
id: results.nodes[i].id,
110-
uri: results.nodes[i].id
129+
uri: results.nodes[i].id,
130+
group: results.nodes[i].group || 'unknown',
131+
count: results.nodes[i].edgeCount || 0,
132+
metadata: results.nodes[i].metadata
111133
},
112134
geometry: {
113135
type: 'Point',
@@ -145,7 +167,9 @@
145167
type: 'Feature',
146168
id: results.edges[i].id,
147169
properties: {
148-
name: results.edges[i].label
170+
name: results.edges[i].label,
171+
strength: results.edges[i].strength || 2,
172+
metadata: results.edges[i].metadata
149173
},
150174
geometry: {
151175
type: 'LineString',
@@ -290,7 +314,7 @@
290314

291315
// Define the map settings.
292316
var tmpMapSettings = mlOlHelper.buildMapSettings();
293-
tmpMapSettings.ptLayer.style = mlOlHelper.createPointStyle;
317+
tmpMapSettings.ptLayer.style = mlOlHelper.createPointCountStyle;
294318
tmpMapSettings.lineLayer = $scope.lineLayer ? $scope.lineLayer : defaultLineLayer;
295319
tmpMapSettings.center.zoom = $scope.zoom ? $scope.zoom : 4;
296320

@@ -370,27 +394,46 @@
370394
'#e8790b', // orange
371395
'#2433d8', // blue
372396
'#bb0be8', // purple
373-
'#17e804' // lime-green
397+
'#17e804' // lime-green
374398
];
375399

376-
var createTextStyle = function(text, fColor) {
377-
var fillColor = '#f70010';
378-
if (fColor) {
379-
fillColor = fColor;
380-
}
400+
// Map for user defined colors based on 'group' property.
401+
var groupColorMap = {};
402+
403+
var createTextStyle = function(text, fColor, sColor, yOffset) {
404+
var fillColor = fColor || '#f70010';
405+
var strokeColor = sColor || 'white';
406+
var yOff = yOffset || 6;
407+
381408
return new ol.style.Text({
382409
textAlign: 'center',
383410
textBaseline: 'top',
384411
font: '12px Arial',
385412
text: text,
386413
fill: new ol.style.Fill({color: fillColor}),
387-
stroke: new ol.style.Stroke({color: 'white', width: 3}),
414+
stroke: new ol.style.Stroke({color: strokeColor, width: 1}),
388415
offsetX: 0,
389-
offsetY: 4,
416+
offsetY: yOff,
390417
rotation: 0
391418
});
392419
};
393420

421+
var createTextCountStyle = function(count, fColor, yOffset, xOffset) {
422+
var fillColor = fColor || '#f70010';
423+
var yOff = yOffset || 6;
424+
var xOff = xOffset || 6;
425+
426+
return new ol.style.Text({
427+
textAlign: 'center',
428+
textBaseline: 'middle',
429+
font: '8pt Arial',
430+
text: count.toString(),
431+
fill: new ol.style.Fill({color: fillColor}),
432+
offsetX: xOff,
433+
offsetY: yOff
434+
});
435+
};
436+
394437
var createClusterTextStyle = function(text) {
395438
return new ol.style.Text({
396439
textAlign: 'center',
@@ -414,15 +457,30 @@
414457
];
415458

416459
geometry.forEachSegment(function(start, end) {
460+
var canvas = document.createElement('canvas');
461+
var render = canvas.getContext('2d');
417462
var dx = end[0] - start[0];
418463
var dy = end[1] - start[1];
419464
var rotation = Math.atan2(dy, dx);
465+
466+
render.strokeStyle = 'black';
467+
render.fillStyle = 'black';
468+
render.lineWidth = 3;
469+
render.beginPath();
470+
render.moveTo(0,0);
471+
render.lineTo(10,10);
472+
render.lineTo(0,20);
473+
render.stroke();
474+
420475
// arrows
421476
styles.push(new ol.style.Style({
422477
geometry: new ol.geom.Point(end),
423478
image: new ol.style.Icon({
424-
src: 'images/arrow-black.png',
425-
anchor: [1.5, 0.5],
479+
img: canvas,
480+
imgSize: [canvas.width, canvas.height],
481+
anchor: [26, 10],
482+
anchorXUnits: 'pixels',
483+
anchorYUnits: 'pixels',
426484
rotateWithView: false,
427485
rotation: -rotation
428486
})
@@ -433,15 +491,92 @@
433491
};
434492

435493
var createPointStyle = function(feature, resolution) {
436-
var radius = 6;
437-
return new ol.style.Style({
438-
image: new ol.style.Circle({
439-
radius: radius,
440-
fill: new ol.style.Fill({color: 'red'}),
441-
stroke: new ol.style.Stroke({color: 'black', width: 1})
442-
}),
443-
text: createTextStyle(feature.get('name'))
444-
});
494+
var radius = 8;
495+
var fillColor = 'red';
496+
if (feature.get('group')) {
497+
var group = feature.get('group');
498+
if (group && groupColorMap[group]) {
499+
fillColor = groupColorMap[group];
500+
}
501+
}
502+
503+
var styles = [
504+
new ol.style.Style({
505+
image: new ol.style.Circle({
506+
radius: radius,
507+
fill: new ol.style.Fill({color: fillColor}),
508+
stroke: new ol.style.Stroke({color: 'black', width: 1})
509+
}),
510+
text: createTextStyle(feature.get('name'), fillColor)
511+
})
512+
];
513+
514+
return styles;
515+
};
516+
517+
var createPointCountStyle = function(feature, resolution) {
518+
var radius = 16;
519+
var fillColor = 'red';
520+
var childCount = feature.get('count') || 0;
521+
var styles = [];
522+
523+
if (feature.get('group')) {
524+
var group = feature.get('group');
525+
if (group && groupColorMap[group]) {
526+
fillColor = groupColorMap[group];
527+
}
528+
}
529+
530+
styles.push(
531+
new ol.style.Style({
532+
image: new ol.style.Circle({
533+
radius: radius,
534+
fill: new ol.style.Fill({color: fillColor}),
535+
stroke: new ol.style.Stroke({color: 'black', width: 1})
536+
}),
537+
text: createTextStyle(feature.get('name'), fillColor, null, radius)
538+
})
539+
);
540+
541+
if (childCount > 0) {
542+
var cRadius = 10;
543+
var textColor = 'white';
544+
var canvas = document.createElement('canvas');
545+
var render = canvas.getContext('2d');
546+
547+
if (childCount >= 100 && childCount < 1000) {
548+
cRadius = 12;
549+
}
550+
else if (childCount >= 1000 && childCount < 10000) {
551+
cRadius = 15;
552+
}
553+
else if (childCount >= 10000) {
554+
cRadius = 17;
555+
}
556+
557+
render.strokeStyle = 'white';
558+
render.fillStyle = '#848484';
559+
render.lineWidth = 1;
560+
render.beginPath();
561+
render.arc(cRadius, cRadius, cRadius, 0, 2 * Math.PI);
562+
render.fill();
563+
render.stroke();
564+
565+
styles.push(
566+
new ol.style.Style({
567+
image: new ol.style.Icon({
568+
img: canvas,
569+
imgSize: [canvas.width, canvas.height],
570+
anchor: [cRadius + 15, 20],
571+
anchorXUnits: 'pixels',
572+
anchorYUnits: 'pixels'
573+
}),
574+
text: createTextCountStyle(childCount, textColor, cRadius - 20, -15)
575+
})
576+
);
577+
}
578+
579+
return styles;
445580
};
446581

447582
var createClusterPointStyle = function(feature, resolution) {
@@ -473,7 +608,7 @@
473608
fill: new ol.style.Fill({color: markerColors[colorIdx]}),
474609
stroke: new ol.style.Stroke({color: 'black', width: strokeWidth, lineDash: lineDash})
475610
}),
476-
text: (count > 1) ? createClusterTextStyle(''+ count) : createTextStyle('')
611+
text: (count > 1) ? createClusterTextStyle(count.toString()) : createTextStyle('')
477612
});
478613
};
479614

@@ -550,14 +685,21 @@
550685
return settings;
551686
};
552687

688+
var setGroupColor = function(group, color) {
689+
groupColorMap[group] = color;
690+
};
691+
553692
return {
554693
createTextStyle: createTextStyle,
694+
createTextCountStyle: createTextCountStyle,
555695
createPointStyle: createPointStyle,
696+
createPointCountStyle: createPointCountStyle,
556697
createClusterPointStyle: createClusterPointStyle,
557698
createClusterTextStyle: createClusterTextStyle,
558699
createLineStyle: createLineStyle,
559700
convertExtent: convertExtent,
560-
buildMapSettings: buildMapSettings
701+
buildMapSettings: buildMapSettings,
702+
setGroupColor: setGroupColor
561703
};
562704
}
563705

dist/ml-ol-maps-ng.min.css

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)