Tuesday, 14 August 2018
checking if an element contains overflow & appling a style
elements that need to be checked to see if they have any overflow have the .mark-if-overflow class applied to them.
function CheckAllCellsForOverflow() {
$(".mark-if-overflow .cell-wrapper").each(function (index) {
if (($(this).prop('scrollWidth') > $(this).width()) || ($(this).prop('scrollHeight') > $(this).height())) {
$(this).addClass('contains-hidden-overflow');
} else {
$(this).removeClass('contains-hidden-overflow');
}
});
}
filtering & sorting json in .net
these guys:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
via the Newtonsoft.Json nuget package.
Entity in database describing a filter:
public partial class DisplayGroupDataFilter
{
public int Id { get; set; }
public int GroupId { get; set; }
public string FilterRoot { get; set; }
public string PropertyNameToFilterOn { get; set; }
public string FilterItemValues { get; set; }
public string FilterType { get; set; }
public bool Active { get; set; }
public virtual DisplayGroup DisplayGroup { get; set; }
}
Entity in database describing a sort:
public partial class DisplayGroupDataSort
{
public int Id { get; set; }
public int GroupId { get; set; }
public string Field { get; set; }
public string Direction { get; set; }
public bool Active { get; set; }
public int Order { get; set; }
public virtual DisplayGroup DisplayGroup { get; set; }
}
the DataService:
/// <summary>
/// applies filters and sort to supplied json data
/// </summary>
/// <param name="data">the json data</param>
/// <param name="dataFilters">collection of filters to apply to data</param>
/// <param name="dataSorts">collection of sorts to apply to data</param>
/// <returns></returns>
private string ApplManipulationsToGroupData(string data, List<DisplayGroupDataFilter> dataFilters, List<DisplayGroupDataSort> dataSorts)
{
JObject jdata = JObject.Parse(data);
foreach (var filter in dataFilters)
{
jdata = ApplyDataFilterToGroupData(jdata, filter);
}
if (dataSorts.Any())
{
ApplySortToGroupData(jdata, dataSorts);
}
return JsonConvert.SerializeObject(jdata);
}
private JObject ApplySortToGroupData(JObject data, List<DisplayGroupDataSort> dataSorts)
{
// get the collection we want to sort our of the data
JArray existing = (JArray)data.SelectToken("Location.Rows");
// create a custom comparer that understands how we want sort elements
var comparer = new JObjectSortComparer(dataSorts);
// sort the collection using our custom comparer
var sorted = existing.AsQueryable().OrderBy(obj => (JObject)obj, comparer);
// put the sorted collection back into the orginal data
data["Location"]["Rows"] = new JArray(sorted);
return data;
}
private JObject ApplyDataFilterToGroupData(JObject data, DisplayGroupDataFilter dataFilter)
{
JObject jobject = data;
string filterRoot = dataFilter.FilterRoot; // the node we want to apply the filter at e.g. "Location.Rows";
string tokenNameToCheck = dataFilter.PropertyNameToFilterOn; // the name of property we want to filter on e.g. "BedId";
string filterType = dataFilter.FilterType; // the type of filter e.g. "Exclude" or "Include";
List<string> filterItems = dataFilter.FilterItemValues.Split(',').ToList(); // new List<string> { "K2", "K3" };
List<JToken> matchedItems = new List<JToken>(); // will contain a list of items that match our filter
// get the collection we want to apply the filter too
var rows = jobject.SelectToken(filterRoot);
// find and make a collection of the items that match our filter
foreach (var item in rows)
{
var value = (string)item.SelectToken(tokenNameToCheck);
if (filterItems.Contains(value))
{
matchedItems.Add(item);
}
}
// remove any of our matched items
if (filterType == "Exclude")
{
foreach (var item in matchedItems)
{
item.Remove();
}
}
// remove anything not in our matched items (i.e. only keep matches)
if (filterType == "Include")
{
var nonMatches = rows.Where(i => !matchedItems.Contains(i)).ToList();
foreach (var item in nonMatches)
{
item.Remove();
}
}
return jobject;
}
The custom comparer that does the actual sorting:
/// <summary>
/// compares two jobjects based on a list of sorts (containing a field name and sort direction) passed into the constructor
/// </summary>
public class JObjectSortComparer : IComparer<JObject>
{
private List<DisplayGroupDataSort> requiredSorts;
public JObjectSortComparer(List<DisplayGroupDataSort> sorts)
{
requiredSorts = sorts;
}
public int Compare(JObject a, JObject b)
{
return DoCompare(a, b, 0);
}
private int DoCompare(JObject a, JObject b, int depth)
{
var fieldName = requiredSorts[depth].Field;
var compareResult = string.Compare((string)a.SelectToken(fieldName), (string)b.SelectToken(fieldName));
if (requiredSorts[depth].Direction.ToLower() == "desc")
{
compareResult = compareResult * -1;
}
if (compareResult == 0 && depth < requiredSorts.Count - 1)
{
return DoCompare(a, b, depth + 1);
}
return compareResult;
}
}
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
via the Newtonsoft.Json nuget package.
Entity in database describing a filter:
public partial class DisplayGroupDataFilter
{
public int Id { get; set; }
public int GroupId { get; set; }
public string FilterRoot { get; set; }
public string PropertyNameToFilterOn { get; set; }
public string FilterItemValues { get; set; }
public string FilterType { get; set; }
public bool Active { get; set; }
public virtual DisplayGroup DisplayGroup { get; set; }
}
Entity in database describing a sort:
public partial class DisplayGroupDataSort
{
public int Id { get; set; }
public int GroupId { get; set; }
public string Field { get; set; }
public string Direction { get; set; }
public bool Active { get; set; }
public int Order { get; set; }
public virtual DisplayGroup DisplayGroup { get; set; }
}
the DataService:
/// <summary>
/// applies filters and sort to supplied json data
/// </summary>
/// <param name="data">the json data</param>
/// <param name="dataFilters">collection of filters to apply to data</param>
/// <param name="dataSorts">collection of sorts to apply to data</param>
/// <returns></returns>
private string ApplManipulationsToGroupData(string data, List<DisplayGroupDataFilter> dataFilters, List<DisplayGroupDataSort> dataSorts)
{
JObject jdata = JObject.Parse(data);
foreach (var filter in dataFilters)
{
jdata = ApplyDataFilterToGroupData(jdata, filter);
}
if (dataSorts.Any())
{
ApplySortToGroupData(jdata, dataSorts);
}
return JsonConvert.SerializeObject(jdata);
}
private JObject ApplySortToGroupData(JObject data, List<DisplayGroupDataSort> dataSorts)
{
// get the collection we want to sort our of the data
JArray existing = (JArray)data.SelectToken("Location.Rows");
// create a custom comparer that understands how we want sort elements
var comparer = new JObjectSortComparer(dataSorts);
// sort the collection using our custom comparer
var sorted = existing.AsQueryable().OrderBy(obj => (JObject)obj, comparer);
// put the sorted collection back into the orginal data
data["Location"]["Rows"] = new JArray(sorted);
return data;
}
private JObject ApplyDataFilterToGroupData(JObject data, DisplayGroupDataFilter dataFilter)
{
JObject jobject = data;
string filterRoot = dataFilter.FilterRoot; // the node we want to apply the filter at e.g. "Location.Rows";
string tokenNameToCheck = dataFilter.PropertyNameToFilterOn; // the name of property we want to filter on e.g. "BedId";
string filterType = dataFilter.FilterType; // the type of filter e.g. "Exclude" or "Include";
List<string> filterItems = dataFilter.FilterItemValues.Split(',').ToList(); // new List<string> { "K2", "K3" };
List<JToken> matchedItems = new List<JToken>(); // will contain a list of items that match our filter
// get the collection we want to apply the filter too
var rows = jobject.SelectToken(filterRoot);
// find and make a collection of the items that match our filter
foreach (var item in rows)
{
var value = (string)item.SelectToken(tokenNameToCheck);
if (filterItems.Contains(value))
{
matchedItems.Add(item);
}
}
// remove any of our matched items
if (filterType == "Exclude")
{
foreach (var item in matchedItems)
{
item.Remove();
}
}
// remove anything not in our matched items (i.e. only keep matches)
if (filterType == "Include")
{
var nonMatches = rows.Where(i => !matchedItems.Contains(i)).ToList();
foreach (var item in nonMatches)
{
item.Remove();
}
}
return jobject;
}
The custom comparer that does the actual sorting:
/// <summary>
/// compares two jobjects based on a list of sorts (containing a field name and sort direction) passed into the constructor
/// </summary>
public class JObjectSortComparer : IComparer<JObject>
{
private List<DisplayGroupDataSort> requiredSorts;
public JObjectSortComparer(List<DisplayGroupDataSort> sorts)
{
requiredSorts = sorts;
}
public int Compare(JObject a, JObject b)
{
return DoCompare(a, b, 0);
}
private int DoCompare(JObject a, JObject b, int depth)
{
var fieldName = requiredSorts[depth].Field;
var compareResult = string.Compare((string)a.SelectToken(fieldName), (string)b.SelectToken(fieldName));
if (requiredSorts[depth].Direction.ToLower() == "desc")
{
compareResult = compareResult * -1;
}
if (compareResult == 0 && depth < requiredSorts.Count - 1)
{
return DoCompare(a, b, depth + 1);
}
return compareResult;
}
}
Tuesday, 22 May 2018
basic application monitor
apparently monitoring is important.
As a developer I like to reinvent the wheel so here's the basic application monitor.
A windows service runs a heap of monitors based on 3 types; website monitor checks for a good http response code, db monitor runs a small select query, healthchecker requests some json from the app (this is a custom thing we do on the apps so the app can report how healthy it is). The service then outputs it all into a big json file on the filesystem. The website then reads the json and displays it.
There's no notifications, there's no history, there's no interface to add more monitors (you have to add via json configuration). The goal is to keep it as simple as possible and be a snapshot of what is happening right now.
nz geohunter - game based on googlemaps api
A game based on googlemaps api.

The difficulty increases when you get three right in a row and decreases when you get one wrong. Harder = smaller population centers, easier = cities.

The difficulty increases when you get three right in a row and decreases when you get one wrong. Harder = smaller population centers, easier = cities.
google maps - how to get co-ordinates for drawing buildings
This will let you draw shapes on the satellite picture and then output the lat lng coordinates of each point of the line. Those co-ordinates can then be saved and used by something else to draw shapes on maps.
This needs the drawing library! Make sure it is on the end of the link to the google maps api e.g. <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing">
Open up the dev tools and after drawing a rectangle, set of lines or a point it should output the co-ordinates to the console.
<body>
<div id="map"></div>
<script>
function initMap() {
var uluru = {
lat: -39.072474,
lng: 174.055992
};
var map = new google.maps.Map(document.getElementById('map'), {
center: uluru,
zoom: 17,
mapTypeId: 'satellite'
});
// this creates the base options for the drawings (polygons/buildings)
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.MARKER,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['marker', 'circle', 'polygon', 'polyline', 'rectangle']
},
markerOptions: {
icon: 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
},
rectangleOptions: {
fillOpacity: 0
},
circleOptions: {
//fillColor: '#ffff00',
fillOpacity: 1,
strokeWeight: 5,
clickable: false,
editable: true,
zIndex: 1
}
});
drawingManager.setMap(map);
// this just outputs the coordinates once a polygon is drawn on the screen
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
console.log(event.type);
if (event.type == 'rectangle') {
var ne = event.overlay.getBounds().getNorthEast();
var sw = event.overlay.getBounds().getSouthWest();
var bounds = "ne: " + ne.lat() + ", sw: " + sw.lat() + ", ne: " + ne.lng() + ", sw: " + sw.lng();
console.log(bounds);
}
if (event.type == 'polyline') {
//var radius = event.overlay.getRadius();
var coordinatesArray = event.overlay.getPath().getArray();
var allCoordinates = "";
for (var i = 0; i < coordinatesArray.length; i++) {
//console.log("new google.maps.LatLng(" + coordinatesArray[i].lat() + ", " + coordinatesArray[i].lng() + "),");
var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
allCoordinates = allCoordinates + coord;
}
console.log(allCoordinates);
}
if (event.type == 'marker') {
var lat = event.overlay.getPosition().lat();
var lng = event.overlay.getPosition().lng();
var coord = "{lat: " + lat + ", lng: " + lng + "},";
console.log(coord);
}
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=[your key here]&libraries=drawing&callback=initMap" async defer></script>
</body>
wow. After you've got the coords and drawn some polygons based off them you can edit the lines by setting editable to true and adding a listener to the path you've created and output the new co-ordinates. This allows for fine tuning of the points of the polygon.
// this is just used when editing the buildings - will output the new paths
buildingDefinition.getPath().addListener('set_at', function(index, event) {
var coordinatesArray = this.getArray();
var allCoordinates = "";
for (var i = 0; i < coordinatesArray.length; i++) {
var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
allCoordinates = allCoordinates + coord;
}
console.log(allCoordinates);
});
This needs the drawing library! Make sure it is on the end of the link to the google maps api e.g. <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing">
Open up the dev tools and after drawing a rectangle, set of lines or a point it should output the co-ordinates to the console.
<body>
<div id="map"></div>
<script>
function initMap() {
var uluru = {
lat: -39.072474,
lng: 174.055992
};
var map = new google.maps.Map(document.getElementById('map'), {
center: uluru,
zoom: 17,
mapTypeId: 'satellite'
});
// this creates the base options for the drawings (polygons/buildings)
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.MARKER,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['marker', 'circle', 'polygon', 'polyline', 'rectangle']
},
markerOptions: {
icon: 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
},
rectangleOptions: {
fillOpacity: 0
},
circleOptions: {
//fillColor: '#ffff00',
fillOpacity: 1,
strokeWeight: 5,
clickable: false,
editable: true,
zIndex: 1
}
});
drawingManager.setMap(map);
// this just outputs the coordinates once a polygon is drawn on the screen
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
console.log(event.type);
if (event.type == 'rectangle') {
var ne = event.overlay.getBounds().getNorthEast();
var sw = event.overlay.getBounds().getSouthWest();
var bounds = "ne: " + ne.lat() + ", sw: " + sw.lat() + ", ne: " + ne.lng() + ", sw: " + sw.lng();
console.log(bounds);
}
if (event.type == 'polyline') {
//var radius = event.overlay.getRadius();
var coordinatesArray = event.overlay.getPath().getArray();
var allCoordinates = "";
for (var i = 0; i < coordinatesArray.length; i++) {
//console.log("new google.maps.LatLng(" + coordinatesArray[i].lat() + ", " + coordinatesArray[i].lng() + "),");
var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
allCoordinates = allCoordinates + coord;
}
console.log(allCoordinates);
}
if (event.type == 'marker') {
var lat = event.overlay.getPosition().lat();
var lng = event.overlay.getPosition().lng();
var coord = "{lat: " + lat + ", lng: " + lng + "},";
console.log(coord);
}
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=[your key here]&libraries=drawing&callback=initMap" async defer></script>
</body>
wow. After you've got the coords and drawn some polygons based off them you can edit the lines by setting editable to true and adding a listener to the path you've created and output the new co-ordinates. This allows for fine tuning of the points of the polygon.
// this is just used when editing the buildings - will output the new paths
buildingDefinition.getPath().addListener('set_at', function(index, event) {
var coordinatesArray = this.getArray();
var allCoordinates = "";
for (var i = 0; i < coordinatesArray.length; i++) {
var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
allCoordinates = allCoordinates + coord;
}
console.log(allCoordinates);
});

Clicking a building will show details:

Doing whatever:

'Ambulance house' and 'Helicopter house' may or may not be the offical names of the buildings.
var buildings = [];
var ict = {
title: "ICT Services",
key: "itServices",
titleCoordinates: { lat: -39.0747770079001, lng: 174.0569919347763 },
description: "Applications, Business Intellignence, Reporting and Development",
coordinates: [{
lat: -39.07470621091739,
lng: 174.05701607465744
}, {
lat: -39.07475202073784,
lng: 174.0570965409279
}, {
lat: -39.074962329077316,
lng: 174.05688732862473
}, {
lat: -39.07491027261602,
lng: 174.0568122267723
}]
}
buildings.push(ict);
etc - add more buildings as required
This mob of co-ordinates is just referenced in the HTML
<script type="text/javascript" src="buildingPersistance.js"></script>
<script type="text/javascript" src="otherPersistance.js"></script>
Ok.
You have to sign up with google api and get a map key. Cram the script tag in your html:
<script src="https://maps.googleapis.com/maps/api/js?key=[your key here]"></script>
Hook up an initialisation method to run when the maps stuff is loaded
google.maps.event.addDomListener(window, 'load', initMap);
You need an initialisation method
function initMap() {
var uluru = {
lat: -39.072474,
lng: 174.055992
};
map = new google.maps.Map(document.getElementById('map'), {
center: uluru,
zoom: 17,
styles: [{
featureType: 'poi',
stylers: [{
visibility: 'off'
}]
}, {
featureType: 'transit',
elementType: 'labels.icon',
stylers: [{
visibility: 'off'
}]
}]
});
// draw any buildings we have
drawBuildings(map, buildings);
etc ...
When creating the map object the center will set the start location of the map
Styles defines what objects you want to show on the map (a heap of stuff shows by default)
Draw the things you want
function drawBuildings(map, buildings) {
for (var i = 0; i < buildings.length; i++) {
var building = buildings[i];
var buildingCoords = [];
for (var c = 0; c < building.coordinates.length; c++) {
var coordinate = building.coordinates[c];
buildingCoords.push(new google.maps.LatLng(coordinate.lat, coordinate.lng));
}
var strokeColor = building.strokeColor ? building.strokeColor : '#000';
var fillColor = building.fillColor ? building.fillColor : '#000';
var buildingDefinition = new google.maps.Polygon({
paths: buildingCoords,
draggable: false,
editable: false,
strokeColor: strokeColor,
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: fillColor,
fillOpacity: 0.35,
originalStrokeColor: strokeColor,
originalFillColor: fillColor,
key: building.key,
description: building.description,
title: building.title
});
buildingDefinition.setMap(map);
Adding a popup for a building
// build the popup information if we have a description for this building
if (buildingDefinition.description) {
buildingDefinition.addListener('click', function(event) {
var contentString = '<div class="info-list-title">' + this.title + "</div>" + this.description;
infowindow.setContent(contentString);
infowindow.setPosition(event.latLng);
infowindow.open(map);
});
}
Creating a label for a building (using maplabel-compiled.js from ... somewhere?)
// create a label for this building
if (building.title && building.titleCoordinates) {
var mapLabel = new MapLabel({
text: building.title,
position: new google.maps.LatLng(building.titleCoordinates.lat, building.titleCoordinates.lng),
map: map,
fontSize: 9
});
mapLabels.push(mapLabel);
}
Subscribe to:
Posts (Atom)



