Example project that can be used to quick start a new csWeb application. Fork me and roll-your-own app to get started.
Source code, Wiki's and issue tracker can all be found here.
We need a few tools to get you going. First of all, install node. We are currently using version 5, and if you're on Windows, please install the msi package, as this will install the node package manager (npm) too. We work with npm version 3.
When you have node and npm installed, install typescript (to compile the TypeScript sources to javascript).
npm i -g typescript
We also need bower to fetch some client libraries. You can install bower using npm.
npm i -g bower
If you don't have git installed, you can also have a look at the bower getting started documentation here
Optionally, you may also install nodemon
and/or http-server
to run a local web server that serves your files. The first as it continuously watches your files and restarts when something changes. The second to run the application stand-alone (without the node server - this is useful if you wish to share your application on a public html folder without running a server).
npm i -g nodemon http-server
Finally, although you can use any text editor to edit the project, we use the free and open source Visual Studio Code, available for Windows, Linux and Mac.
First, get the code in a local folder of your choice, either by forking this project or by downloading the zip file and unpacking it in a folder. Next, install all dependencies, compile and run node:
npm i
cd public && bower i
cd ..
tsc -p .
node server.js
I got error messages during the first step (npm i): "TRACKER : error TRK0005: Failed to locate: "CL.exe"". It seems that CL.exe belongs to Visual Studio. So, is seems to be another requisite to have Visual Studio installed. Or not. The rest of the steps were completed without errors and the server/app seems to work ok. (comment 30 Dec 15 by Reinier Sterkenburg)
Alternatively, replace the last command with nodemon server.js
or go to the public folder and run http-server
.
Visit http://localhost:3003 to see the application running.
If everything went well, you should now have your application up and running. However, most likely you don't want to call your application csWeb-example, so here are a few steps to make the app your own.
In your project, edit public/data/projects/projects.json and replace the projects property with the following (you can load multiple projects, but for now, let's start with just one - optionally, you can leave the others so you can switch between them and see more examples of what is possible):
"projects": [{
"title": "MY_TITLE",
"url": "data/projects/MY_PROJECT_DATA/project.json",
"dynamic": true
}]
Where MY_TITLE
and MY_PROJECT_DATA
should be replaced with your names. You also need to rename the existing folder, named MY_PROJECT_DATA
, to the folder name you selected.
Edit data/projects/MY_PROJECT_DATA/project.json
and (search and) replace MY_TITLE
, and MY_PROJECT_DATA
. Optionally, you can also replace the logo property.
By now, you should be able to reload your application in the browser and see your title. However, the data is still the same as before. Edit data/projects/MY_PROJECT_DATA/project.json
and look for groups
and layers
. The group
represents a group of map layers. Edit its id, description, title. Next, edit the layers that belong in this group (note that all styling, filtering, and clustering operations are performed on a group level). For each layer, specify its id, title, description, and reference to the GEOJSON data (we also support KML, topojson, and grid files - see the other project.json files for examples). You can visit geojson.io to create a GEOJSON file manually.
A GEOJSON file only contains data (as key-value pairs in the properties field). To make it look pretty, we specify exactly how each feature should be displayed when selected, as well as each feature's property. The layer's typeUrl
specifies the markup that is being used, and the defaultFeatureType
specifies the default feature type that is used (unless overruled when a feature has a property named featureTypeId
).
So go to the data/resourceTypes
folder and start editing POLYGON_FEATURE.json
and POINT_FEATURE.json
. Each key in featureTypes
refers to the defaultFeatureType
(or featureTypeId
) in the project.json
file. Replace its name
, iconUri
, nameLabel
(refers to the feature's property that contains its name, which is shown upon hovering) and propertyTypeKeys
. The latter requires some explanation: A feature can have many properties. However, we only display those that you explicitly reference in the propertyTypeKeys
.
Finally, you can edit each the style that is used for each property. Edit the appropriate key in the propertyTypeData
, where the label
refers to the property key in the data (e.g. people
when you have a property "feature.properties.people": 5000
in the data file), its title
, type (e.g. text, textarea, number, boolean, options
). For number
, you may specify the stringFormat
(using the .NET syntax).
For more information on FeatureType
and PropertyType
formats, see the csWeb page.
Switch to admin mode
(using the top right icon, showing a user with a bookcase), and click on the gear icon to edit a style.
TODO...
If you wish to change the underlying csWeb framework, you also need to checkout csWeb. In csWeb, create npm and bower links, which you can subsequently use in csWeb-example.
I assume that csWeb-example and csWeb share the same parent folder. In csWeb, do the following:
gulp init
bower link
cd dist-npm && npm link
And in csWeb-example, run:
npm link csweb
cd public && bower link csweb
tsc
node server.js
If you wish to create a new widget or service, and don't want to add it to the csWeb framework (e.g. because it is very specific), you can extend the example application yourself. For example, when creating a new widget and service, follow these steps.
- Create a myWidgets folder, and in this folder.
- Create a MyWidget.ts file for registering your widget.
- Create a MyWidgetCtrl.ts and MyWidget.tpl.html file which contains the widget controller and its template.
- Create a MyWidgetSvc.ts file for having a persistant service (you can also use it to register your widget, or alternatively, you can do that in the AppCtrl (in app/app.ts).
- Add the TypeScript files to tsconfig.json.
- Add the generated JavaScript files to the index.html (before the app/app.js).
- In the AppCtrl, inject your MyWidgetSvc so it is instantiated.
Some examples:
MyWidget.ts
:
module MyWidget {
/**
* Config
*/
var moduleName = 'csComp';
/**
* Module
*/
export var myModule;
try {
myModule = angular.module(moduleName);
} catch (err) {
// named module does not exist, so create one
myModule = angular.module(moduleName, []);
}
/**
* Directive to display the available map layers.
*/
myModule.directive('mywidget', [function() : ng.IDirective {
return {
restrict : 'E', // E = elements, other options are A=attributes and C=classes
scope : {
}, // isolated scope, separated from parent. Is however empty, as this directive is self contained by using the messagebus.
templateUrl: 'MyWidget/MyWidget.tpl.html',
replace : true, // Remove the directive from the DOM
transclude : false, // Add elements and attributes to the template
controller : MyWidgetCtrl
}
}
]);
}
MyWidgetSvc.ts
:
module MyWidget {
export class MyWidgetSvc {
static $inject = [
'$rootScope',
'layerService',
'messageBusService',
'mapService',
'dashboardService',
'$http'
];
constructor(
private $rootScope: ng.IRootScopeService,
private layerService: csComp.Services.LayerService,
private messageBusService: csComp.Services.MessageBusService,
private mapService: csComp.Services.MapService,
private dashboardService: csComp.Services.DashboardService,
private $http: ng.IHttpService) {
this.dashboardService.widgetTypes['mywidget'] = <csComp.Services.IWidget> {
id: 'mywidget',
icon: 'images/myWidgetIcon.png',
description: 'Show MyWidget widget'
}
}
}
/**
* Register service
*/
var moduleName = 'csComp';
/**
* Module
*/
export var myModule;
try {
myModule = angular.module(moduleName);
} catch (err) {
// named module does not exist, so create one
myModule = angular.module(moduleName, []);
}
myModule.service('myWidgetService', MyWidget.MyWidgetSvc);
}
MyWidgetCtrl.ts
:
module hmf {
export class MyWidgetData {
title: string;
}
export interface IMyWidgetScope extends ng.IScope {
vm: MyWidgetCtrl;
data: MyWidgetData;
minimized: boolean;
}
export class MyWidgetCtrl {
private scope: IMyWidgetScope;
private widget: csComp.Services.IWidget;
private parentWidget: JQuery;
public static $inject = [
'$scope',
'$timeout',
'layerService',
'messageBusService',
'mapService'
];
constructor(
private $scope: IMyWidgetScope,
private $timeout: ng.ITimeoutService,
private $layerService: csComp.Services.LayerService,
private $messageBus: csComp.Services.MessageBusService,
private $mapService: csComp.Services.MapService
) {
$scope.vm = this;
var par = <any>$scope.$parent;
this.widget = par.widget;
$scope.data = <MyWidgetData>this.widget.data;
$scope.data.mdText = $scope.data.content;
$scope.minimized = false;
this.parentWidget = $('#' + this.widget.elementId).parent();
if (typeof $scope.data.featureTypeName !== 'undefined' && typeof $scope.data.dynamicProperties !== 'undefined' && $scope.data.dynamicProperties.length > 0) {
// Hide widget
this.parentWidget.hide();
this.$messageBus.subscribe('feature', (action: string, feature: csComp.Services.IFeature) => {
switch (action) {
case 'onFeatureDeselect':
case 'onFeatureSelect':
this.selectFeature(feature);
break;
default:
break;
}
});
}
if (typeof $scope.data.url === 'undefined') return;
$.get($scope.data.url, (md) => {
$timeout(() => {
$scope.data.content = $scope.data.mdText = md;
}, 0);
});
}
private selectFeature(feature: csComp.Services.IFeature) {
}
}
}