Skip to content

Commit

Permalink
Fix geosolutions-it#1314 Support to print vector layer (leaflet)
Browse files Browse the repository at this point in the history
 - Support vector style for leaflet layers
 - Conversion to OL2 style format
 - add reprojectGeoJson utility method to CoordinateUtils

Missing:
 - Print preview support
 - Markers
  • Loading branch information
offtherailz committed Dec 1, 2016
1 parent 9cdec35 commit 4359652
Show file tree
Hide file tree
Showing 5 changed files with 319 additions and 5 deletions.
92 changes: 88 additions & 4 deletions web/client/utils/CoordinatesUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,49 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var Proj4js = require('proj4');
var assign = require('object-assign');
var {isArray, flattenDeep, chunk} = require('lodash');
const Proj4js = require('proj4');
const proj4 = Proj4js;
const assign = require('object-assign');
const {isArray, flattenDeep, chunk, cloneDeep} = require('lodash');
// Checks if `list` looks like a `[x, y]`.
function isXY(list) {
return list.length >= 2 &&
typeof list[0] === 'number' &&
typeof list[1] === 'number';
}
function traverseCoords(coordinates, callback) {
if (isXY(coordinates)) return callback(coordinates);
return coordinates.map(function(coord) { return traverseCoords(coord, callback); });
}

var CoordinatesUtils = {
function traverseGeoJson(geojson, leafCallback, nodeCallback) {
if (geojson === null) return geojson;

let r = cloneDeep(geojson);

if (geojson.type === 'Feature') {
r.geometry = traverseGeoJson(geojson.geometry, leafCallback, nodeCallback);
} else if (geojson.type === 'FeatureCollection') {
r.features = r.features.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); });
} else if (geojson.type === 'GeometryCollection') {
r.geometries = r.geometries.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); });
} else {
if (leafCallback) leafCallback(r);
}

if (nodeCallback) nodeCallback(r);

return r;
}

function determineCrs(crs) {
if (typeof crs === 'string' || crs instanceof String) {
return Proj4js.defs(crs) ? new Proj4js.Proj(crs) : null;
}
return crs;
}

const CoordinatesUtils = {
getUnits: function(projection) {
const proj = new Proj4js.Proj(projection);
return proj.units || 'degrees';
Expand All @@ -27,6 +65,52 @@ var CoordinatesUtils = {
}
return null;
},
/**
* Reprojects a geojson from a crs into another
*/
reprojectGeoJson: function(geojson, fromParam = "EPSG:4326", toParam = "EPSG:4326") {
let from = fromParam;
let to = toParam;
if (typeof from === 'string') {
from = determineCrs(from);
}
if (typeof to === 'string') {
to = determineCrs(to);
}
let transform = proj4(from, to);

return traverseGeoJson(geojson, (gj) => {
// No easy way to put correct CRS info into the GeoJSON,
// and definitely wrong to keep the old, so delete it.
if (gj.crs) {
delete gj.crs;
}
gj.coordinates = traverseCoords(gj.coordinates, (xy) => {
return transform.forward(xy);
});
}, (gj) => {
if (gj.bbox) {
// A bbox can't easily be reprojected, just reprojecting
// the min/max coords definitely will not work since
// the transform is not linear (in the general case).
// Workaround is to just re-compute the bbox after the
// transform.
gj.bbox = (() => {
let min = [Number.MAX_VALUE, Number.MAX_VALUE];
let max = [-Number.MAX_VALUE, -Number.MAX_VALUE];
traverseGeoJson(gj, function(_gj) {
traverseCoords(_gj.coordinates, function(xy) {
min[0] = Math.min(min[0], xy[0]);
min[1] = Math.min(min[1], xy[1]);
max[0] = Math.max(max[0], xy[0]);
max[1] = Math.max(max[1], xy[1]);
});
});
return [min[0], min[1], max[0], max[1]];
})();
}
});
},
normalizePoint: function(point) {
return {
x: point.x || 0.0,
Expand Down
103 changes: 103 additions & 0 deletions web/client/utils/PrintUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
const CoordinatesUtils = require('./CoordinatesUtils');
const MapUtils = require('./MapUtils');


const {isArray} = require('lodash');

const url = require('url');
Expand All @@ -17,6 +18,10 @@ const defaultScales = MapUtils.getGoogleMercatorScales(0, 21);

const assign = require('object-assign');

const getGeomType = function(layer) {
return (layer.features && layer.features[0]) ? layer.features[0].geometry.type : undefined;
};

const PrintUtils = {
normalizeUrl: (input) => {
let result = isArray(input) ? input[0] : input;
Expand Down Expand Up @@ -140,6 +145,23 @@ const PrintUtils = {
]
})
},
vector: {
map: (layer, spec) => ({
type: 'Vector',
name: layer.name,
"opacity": layer.opacity || 1.0,
styleProperty: "ms_style",
styles: {
1: PrintUtils.toOpenLayers2Style(layer, layer.style)
},
geoJson: CoordinatesUtils.reprojectGeoJson({
type: "FeatureCollection",
features: layer.features.map( f => ({...f, properties: {...f.properties, ms_style: 1}}))
},
"EPSG:4326",
spec.projection)
})
},
osm: {
map: () => ({
"baseURL": "http://a.tile.openstreetmap.org/",
Expand Down Expand Up @@ -220,6 +242,87 @@ const PrintUtils = {
]
})
}
},
/**
* Useful for print (Or generic Openlayers 2 conversion style)
*/
toOpenLayers2Style: function(layer, style) {
if (!style) {
return PrintUtils.getOlDefaultStyle(layer);
}
// commented the available options.
return {
"fillColor": style.fillColor,
"fillOpacity": style.fillOpacity,
// "rotation": "30",
// "graphicName": "circle",
// "graphicOpacity": 0.4,
"pointRadius": style.radius,
"strokeColor": style.color,
"strokeOpacity": style.opacity,
"strokeWidth": style.weight
// "strokeLinecap": "round",
// "strokeDashstyle": "dot",
// "fontColor": "#000000",
// "fontFamily": "sans-serif",
// "fontSize": "12px",
// "fontStyle": "normal",
// "fontWeight": "bold",
// "haloColor": "#123456",
// "haloOpacity": "0.7",
// "haloRadius": "3.0",
// "label": "${name}",
// "labelAlign": "cm",
// "labelRotation": "45",
// "labelXOffset": "-25.0",
// "labelYOffset": "-35.0"
};
},
/**
* Provides the default style for
* each vector type.
*/
getOlDefaultStyle(layer) {
switch (getGeomType(layer)) {
case 'Polygon':
case 'MultiPolygon': {
return {
"fillColor": "#0000FF",
"fillOpacity": 0.1,
"strokeColor": "#0000FF",
"strokeOpacity": 1,
"strokeWidth": 3
};
}
case 'MultiLineString':
case 'LineString':
return {
"strokeColor": "#0000FF",
"strokeOpacity": 1,
"strokeWidth": 3
};
case 'Point':
case 'MultiPoint': {
return {
"fillColor": "#FF0000",
"fillOpacity": 0,
"strokeColor": "#FF0000",
"pointRadius": 5,
"strokeOpacity": 1,
"strokeWidth": 1
};
}
default: {
return {
"fillColor": "#0000FF",
"fillOpacity": 0.1,
"strokeColor": "#0000FF",
"pointRadius": 5,
"strokeOpacity": 1,
"strokeWidth": 1
};
}
}
}
};

Expand Down
29 changes: 29 additions & 0 deletions web/client/utils/__tests__/CoordinatesUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,33 @@ describe('CoordinatesUtils', () => {
expect(CoordinatesUtils.getCompatibleSRS('EPSG:3857', {'EPSG:900913': true, 'EPSG:3857': true})).toBe('EPSG:3857');
expect(CoordinatesUtils.getCompatibleSRS('EPSG:3857', {'EPSG:3857': true})).toBe('EPSG:3857');
});
it('test reprojectGeoJson', () => {
const testPoint = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [
-112.50042920000001,
42.22829164089942
]
},
properties: {
"serial_num": "12C324776"
},
id: 0
}
]
};
const reprojectedTestPoint = CoordinatesUtils.reprojectGeoJson(testPoint, "EPSG:4326", "EPSG:900913");
expect(reprojectedTestPoint).toExist();
expect(reprojectedTestPoint.features).toExist();
expect(reprojectedTestPoint.features[0]).toExist();
expect(reprojectedTestPoint.features[0].type).toBe("Feature");
expect(reprojectedTestPoint.features[0].geometry.type).toBe("Point");
expect(reprojectedTestPoint.features[0].geometry.coordinates[0]).toBe(-12523490.492568726);
expect(reprojectedTestPoint.features[0].geometry.coordinates[1]).toBe(5195238.005360028);
});
});
92 changes: 91 additions & 1 deletion web/client/utils/__tests__/PrintUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
const expect = require('expect');
const PrintUtils = require('../PrintUtils');
const {isEqual} = require('lodash');

const layer = {
url: "http://mygeoserver",
Expand All @@ -15,7 +16,74 @@ const layer = {
params: {myparam: "myvalue"}
};


const vectorLayer = {
"type": "vector",
"visibility": true,
"group": "Local shape",
"id": "web2014all_mv__14",
"name": "web2014all_mv",
"hideLoading": true,
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-112.50042920000001,
42.22829164089942
]
},
"properties": {
"serial_num": "12C324776"
},
"id": 0
}
],
"style": {
"weight": 3,
"radius": 10,
"opacity": 1,
"fillOpacity": 0.1,
"color": "rgb(0, 0, 255)",
"fillColor": "rgb(0, 0, 255)"
}
};
const mapFishVectorLayer = {
"type": "Vector",
"name": "web2014all_mv",
"opacity": 1,
"styleProperty": "ms_style",
"styles": {
"1": {
"fillColor": "rgb(0, 0, 255)",
"fillOpacity": 0.1,
"pointRadius": 10,
"strokeColor": "rgb(0, 0, 255)",
"strokeOpacity": 1,
"strokeWidth": 3
}
},
"geoJson": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-12523490.492568726,
5195238.005360028
]
},
"properties": {
"serial_num": "12C324776",
"ms_style": 1
},
"id": 0
}
]
}
};
describe('PrintUtils', () => {

it('custom params are applied to wms layers', () => {
Expand All @@ -26,4 +94,26 @@ describe('PrintUtils', () => {
expect(specs[0].customParams.myparam).toExist();
expect(specs[0].customParams.myparam).toBe("myvalue");
});
it('vector layer generation for print', () => {
const specs = PrintUtils.getMapfishLayersSpecification([vectorLayer], {projection: "EPSG:900913"}, 'map');
expect(specs).toExist();
expect(specs.length).toBe(1);
expect(isEqual(specs[0], mapFishVectorLayer)).toBe(true);
});
it('vector layer default point style', () => {
const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Point"}}]});
expect(style).toExist();
expect(style.pointRadius).toBe(5);
});
it('vector layer default polygon style', () => {
const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Polygon"}}]});
expect(style).toExist();
expect(style.strokeWidth).toBe(3);

});
it('vector layer default line style', () => {
const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "LineString"}}]});
expect(style).toExist();
expect(style.strokeWidth).toBe(3);
});
});
8 changes: 8 additions & 0 deletions web/client/utils/openlayers/StyleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ const toVectorStyle = function(layer, style) {
if (style.marker && (geomT === 'Point' || geomT === 'MultiPoint')) {
newLayer.styleName = "marker";
}else {
newLayer.style = {
weight: style.width,
radius: style.radius,
opacity: style.color.a,
fillOpacity: style.fill.a,
color: getColor(style.color),
fillColor: getColor(style.fill)
};
let stroke = new ol.style.Stroke({
color: getColor(style.color),
width: style.width
Expand Down

0 comments on commit 4359652

Please sign in to comment.