diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..91e9c37 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**/node_modules +**/build +**/public diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..30a7835 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:14.04 + +# set up our node install +RUN apt-get install -y software-properties-common && add-apt-repository -y ppa:ubuntu-toolchain-r/test + +RUN apt-get update && apt-get install -y \ + checkinstall \ + g++ \ + libstdc++-5-dev \ + pkg-config \ + libcairo2-dev \ + libjpeg8-dev \ + libgif-dev \ + libpango1.0-dev \ + curl + +RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - && apt-get install -y --force-yes nodejs + +RUN npm install -g yarn + +WORKDIR /usr/local/tiler +COPY . . + +RUN yarn --force + +CMD node server.js diff --git a/config.js b/config.js index d5d8808..6d0caf8 100644 --- a/config.js +++ b/config.js @@ -19,6 +19,7 @@ var canopyBoundarySql = [ module.exports = { "filterQueryArgumentName": "q", + "showTreeCondition": "showTreeCondition", "displayQueryArgumentName": "show", "restrictFeatureQueryArgumentName": "restrict", // This is the column name of the hstore column used for scalar udfs @@ -29,7 +30,7 @@ module.exports = { "point": "the_geom_webmercator", "polygon": "stormwater_polygonalmapfeature.polygon" }, - "base": "feature_type, treemap_tree.id AS tree_id", + "base": "feature_type, treemap_tree.id AS tree_id, <%= importerField %> as importer_id, <%= conditionField %> as condition", "polygon": "feature_type", "utfGrid": "feature_type, treemap_mapfeature.id AS id" }, @@ -74,6 +75,10 @@ module.exports = { "depends": ["mapFeature"], "sql": "LEFT OUTER JOIN treemap_mapfeaturephoto ON treemap_mapfeature.id = treemap_mapfeaturephoto.map_feature_id" }, + "treeRowImport": { + "depends": ["mapFeature", "tree"], + "sql": "JOIN importer_treeimportrow on importer_treeimportrow.plot_id = treemap_tree.plot_id" + }, "udf": { // Despite mapFeature not being referenced in the "sql" property it will // be used in the filter for udf and so it is required @@ -88,6 +93,7 @@ module.exports = { }, "treeDisplayFilters": ["EmptyPlot", "Tree"], "boundaryGrainstoreSql": "SELECT the_geom_webmercator FROM treemap_boundary JOIN treemap_instance_boundaries ON treemap_instance_boundaries.boundary_id = treemap_boundary.id WHERE treemap_instance_boundaries.instance_id=<%= instanceid %> AND treemap_boundary.searchable=true", + "boundaryLayerGrainstoreSql": "SELECT the_geom_webmercator, category, name FROM treemap_boundary WHERE treemap_boundary.category='<%= category %>' AND treemap_boundary.searchable=true", "getBoundarySql" : "SELECT the_geom_webmercator FROM treemap_boundary WHERE id=<%= boundaryId %>", "canopyBoundarySql": canopyBoundarySql, "showAtZoomSql": "(treemap_mapfeature.hide_at_zoom IS NULL OR treemap_mapfeature.hide_at_zoom < <%= zoom %>)", @@ -105,7 +111,8 @@ module.exports = { "bioswale": "stormwater_bioswale", "species": "treemap_species", "mapFeaturePhoto": "treemap_mapfeaturephoto", - "udf": "treemap_userdefinedcollectionvalue" + "udf": "treemap_userdefinedcollectionvalue", + "treeRowImport": "importer_treeimportrow" }, "udfcTemplates": { "tree": "treemap_userdefinedcollectionvalue.field_definition_id=<%= fieldDefId %> AND treemap_userdefinedcollectionvalue.model_id=treemap_tree.id", diff --git a/makeSql.js b/makeSql.js index e73a341..09e4b0d 100644 --- a/makeSql.js +++ b/makeSql.js @@ -40,7 +40,8 @@ var utils = require('./filterObjectUtils'); // Assumes that instanceid is an integer, ready to be plugged // directly into SQL function makeSqlForMapFeatures(filterString, displayString, restrictFeatureString, instanceid, - zoom, isUtfGridRequest, isPolygonRequest, instanceConfig) { + zoom, isUtfGridRequest, isPolygonRequest, instanceConfig, + showImportedTrees, showTreeCondition) { var geom_spec = config.sqlForMapFeatures.fields.geom, geom_field = isPolygonRequest ? geom_spec.polygon : geom_spec.point, parsedFilterObject = filterString ? JSON.parse(filterString) : {}, @@ -85,7 +86,13 @@ function makeSqlForMapFeatures(filterString, displayString, restrictFeatureStrin } else if (isPolygonRequest) { otherFields = config.sqlForMapFeatures.fields.polygon; } else { - otherFields = config.sqlForMapFeatures.fields.base; + // we only sometimes want different colors for imported trees + var importerField = showImportedTrees ? 'importer_treeimportrow.id' : 'null'; + var conditionField = showTreeCondition ? 'treemap_tree.udfs -> \'Condition\'' : 'null'; + otherFields = _.template(config.sqlForMapFeatures.fields.base)({ + 'importerField': importerField, + 'conditionField': conditionField + }); } geom_field = util.format("%s AS %s", @@ -122,6 +129,12 @@ function makeSqlForBoundaries(instanceid) { }); } +function makeSqlForBoundariesLayers(category) { + return _.template(config.boundaryLayerGrainstoreSql)({ + category: category + }); +} + function makeSqlForCanopyBoundaries(instanceid, canopy_min, canopy_max, category) { return _.template(config.canopyBoundarySql)({ instanceid: instanceid, @@ -134,5 +147,6 @@ function makeSqlForCanopyBoundaries(instanceid, canopy_min, canopy_max, category exports = module.exports = { makeSqlForMapFeatures: makeSqlForMapFeatures, makeSqlForCanopyBoundaries: makeSqlForCanopyBoundaries, - makeSqlForBoundaries: makeSqlForBoundaries + makeSqlForBoundaries: makeSqlForBoundaries, + makeSqlForBoundariesLayers: makeSqlForBoundariesLayers }; diff --git a/server.js b/server.js index 020c915..ec1a097 100644 --- a/server.js +++ b/server.js @@ -23,6 +23,7 @@ var ws; var styles = { boundary: fs.readFileSync('style/boundary.mms', {encoding: 'utf-8'}), + boundaryCategory: fs.readFileSync('style/boundaryCategory.mms', {encoding: 'utf-8'}), canopy: fs.readFileSync('style/canopy.mms', {encoding: 'utf-8'}), mapFeature: fs.readFileSync('style/mapFeature.mms', {encoding: 'utf-8'}), uncoloredMapFeature: fs.readFileSync('style/uncoloredMapFeature.mms', {encoding: 'utf-8'}), @@ -97,19 +98,27 @@ var windshaftConfig = { table = req.params.table; zoom = req.params.z; isPolygonRequest = (table === 'stormwater_polygonalmapfeature'); - if (table === 'treemap_mapfeature' || isPolygonRequest) { + if (table === 'treemap_mapfeature' || isPolygonRequest || table === 'importer_treerowimport') { filterString = req.query[config.filterQueryArgumentName]; displayString = req.query[config.displayQueryArgumentName]; restrictFeatureString = req.query[config.restrictFeatureQueryArgumentName]; isUtfGridRequest = (req.params.format === 'grid.json'); - req.params.sql = makeSql.makeSqlForMapFeatures(filterString, - displayString, - restrictFeatureString, - instanceid, - zoom, - isUtfGridRequest, - isPolygonRequest, - req.instanceConfig); + + var showImportedTrees = table === 'importer_treerowimport'; + var showTreeCondition = req.query[config.showTreeCondition] === 'true'; + + req.params.sql = makeSql.makeSqlForMapFeatures( + filterString, + displayString, + restrictFeatureString, + instanceid, + zoom, + isUtfGridRequest, + isPolygonRequest, + req.instanceConfig, + showImportedTrees, + showTreeCondition + ); if (isPolygonRequest) { req.params.style = styles.polygonalMapFeature; } else if (isUtfGridRequest) { @@ -117,6 +126,10 @@ var windshaftConfig = { } else { req.params.style = styles.mapFeature; } + } else if (table === 'treemap_boundary' && 'category' in req.query) { + category = req.query.category; + req.params.sql = makeSql.makeSqlForBoundariesLayers(category); + req.params.style = styles.boundaryCategory; } else if (table === 'treemap_boundary' && instanceid) { req.params.sql = makeSql.makeSqlForBoundaries(instanceid); req.params.style = styles.boundary; diff --git a/style/boundaryCategory.mms b/style/boundaryCategory.mms new file mode 100644 index 0000000..816c31a --- /dev/null +++ b/style/boundaryCategory.mms @@ -0,0 +1,80 @@ +#treemap_boundary { + ::case { + line-width: 1; + line-color:#000; + line-opacity: 0.6; + + [category="Neighborhood"] { + line-width: 5; + line-color:#ddd; + line-opacity: 0.7; + } + [category="Main Neighborhood"] { + line-width: 5; + line-color:#ddd; + line-opacity: 0.7; + } + [category="Ward"] { + line-width: 5; + line-color:#ddd; + line-opacity: 0.7; + } + } + + ::fill { + line-width: 0.75; + line-color: #000; + line-opacity: 0.8; + polygon-fill: #55A9F2; + polygon-opacity: 0.2; + + [category="Parcels"] { + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Zones"] { + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="SID"] { + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Park"] { + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Main Neighborhood"] { + polygon-fill: #55A9F2; + polygon-opacity: 0.2; + } + + [category="Neighborhood"] { + polygon-fill: #557CF2; + polygon-opacity: 0.2; + } + + [category="Ward"] { + polygon-fill: #55A9F2; + polygon-opacity: 0.2; + } + } + + ::label { + text-name: '[name]'; + text-face-name: 'DejaVu Sans Bold'; + text-fill: #000; + text-size: 12; + text-halo-fill: rgba(255, 255, 255, 0.5); + text-halo-radius: 1; + text-placement: interior; + text-avoid-edges: false; + + [zoom < 14] { text-name: ''; } + [category="Parcels"] { text-name: ''; } + } +} diff --git a/style/mapFeature.mms b/style/mapFeature.mms index f4685e9..12449c4 100644 --- a/style/mapFeature.mms +++ b/style/mapFeature.mms @@ -7,6 +7,15 @@ @empty_plot_fill_color: #E1C6FF; @empty_plot_stroke_color: #D4B5F9; +@imported_tree_fill_color: #85B4ED; +@imported_tree_stroke_color: #6E9AE0; + +@tree_unhealthy_fill_color: #8B1002; +@tree_dead_fill_color: #303031; + +@tree_unhealthy_stroke_color: #B5554A; +@tree_dead_stroke_color: #646469; + @gsi_fill_color: #388E8E; @tree_gsi_border_color: #b6ce78; @@ -18,6 +27,15 @@ [tree_id=null][feature_type="Plot"] { marker-fill: @empty_plot_fill_color; } + [tree_id!=null][feature_type="Plot"][importer_id!=null] { + marker-fill: @imported_tree_fill_color; + } + [tree_id!=null][feature_type="Plot"][condition="Dead"] { + marker-fill: @tree_dead_fill_color; + } + [tree_id!=null][feature_type="Plot"][condition="Unhealthy"] { + marker-fill: @tree_unhealthy_fill_color; + } [feature_type!="Plot"] { marker-fill: @gsi_fill_color; @@ -34,6 +52,15 @@ [tree_id!=null][feature_type="Plot"][zoom >= 15] { marker-line-color: @tree_gsi_border_color; } + [tree_id!=null][feature_type="Plot"][zoom >= 15][importer_id!=null] { + marker-line-color: @imported_tree_stroke_color; + } + [tree_id!=null][feature_type="Plot"][zoom >= 15][condition="Dead"] { + marker-line-color: @tree_dead_stroke_color; + } + [tree_id!=null][feature_type="Plot"][zoom >= 15][condition="Unhealthy"] { + marker-line-color: @tree_unhealthy_stroke_color; + } [feature_type!="Plot"][zoom >= 15] { marker-line-color: @tree_gsi_border_color; }