From 617ba0b3cef8516f06b9488b8c03744ec85ed91b Mon Sep 17 00:00:00 2001 From: Tom Z Date: Sun, 28 Jun 2020 22:07:55 -0400 Subject: [PATCH 1/6] feature-imported-rows, add the tiler features to show imported rows --- config.js | 9 +++++++-- makeSql.js | 6 ++++-- server.js | 24 +++++++++++++++--------- style/mapFeature.mms | 9 +++++++++ 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/config.js b/config.js index d5d8808..2d48395 100644 --- a/config.js +++ b/config.js @@ -29,7 +29,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", "polygon": "feature_type", "utfGrid": "feature_type, treemap_mapfeature.id AS id" }, @@ -74,6 +74,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 @@ -105,7 +109,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..84a6fb8 100644 --- a/makeSql.js +++ b/makeSql.js @@ -40,7 +40,7 @@ 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) { var geom_spec = config.sqlForMapFeatures.fields.geom, geom_field = isPolygonRequest ? geom_spec.polygon : geom_spec.point, parsedFilterObject = filterString ? JSON.parse(filterString) : {}, @@ -85,7 +85,9 @@ 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'; + otherFields = _.template(config.sqlForMapFeatures.fields.base)({'importerField': importerField}); } geom_field = util.format("%s AS %s", diff --git a/server.js b/server.js index c7e5d78..dd07404 100644 --- a/server.js +++ b/server.js @@ -96,19 +96,25 @@ 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'; + + req.params.sql = makeSql.makeSqlForMapFeatures( + filterString, + displayString, + restrictFeatureString, + instanceid, + zoom, + isUtfGridRequest, + isPolygonRequest, + req.instanceConfig, + showImportedTrees + ); if (isPolygonRequest) { req.params.style = styles.polygonalMapFeature; } else if (isUtfGridRequest) { diff --git a/style/mapFeature.mms b/style/mapFeature.mms index f4685e9..e147fd6 100644 --- a/style/mapFeature.mms +++ b/style/mapFeature.mms @@ -7,6 +7,9 @@ @empty_plot_fill_color: #E1C6FF; @empty_plot_stroke_color: #D4B5F9; +@imported_tree_fill_color: #85B4ED; +@imported_tree_stroke_color: #6E9AE0; + @gsi_fill_color: #388E8E; @tree_gsi_border_color: #b6ce78; @@ -18,6 +21,9 @@ [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; + } [feature_type!="Plot"] { marker-fill: @gsi_fill_color; @@ -34,6 +40,9 @@ [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; + } [feature_type!="Plot"][zoom >= 15] { marker-line-color: @tree_gsi_border_color; } From 2ba21e652c6c7203f1075a09fc005a441d88ed82 Mon Sep 17 00:00:00 2001 From: Tom Z Date: Sun, 5 Jul 2020 21:29:45 -0400 Subject: [PATCH 2/6] Add a Dockerfile and dockerignore --- .dockerignore | 3 +++ Dockerfile | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile 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..d26996d --- /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 nodejs + +RUN npm install -g yarn + +WORKDIR /usr/local/tiler +COPY . . + +RUN yarn --force + +CMD node server.js From a5c9a436c44d3b52dcdfad4b54ee9daf4d878950 Mon Sep 17 00:00:00 2001 From: Tom Z Date: Sun, 9 Aug 2020 22:12:53 -0400 Subject: [PATCH 3/6] 9 - handle layers for categories --- config.js | 1 + makeSql.js | 9 ++++- server.js | 5 +++ style/boundaryCategory.mms | 83 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 style/boundaryCategory.mms diff --git a/config.js b/config.js index 2d48395..a99bf92 100644 --- a/config.js +++ b/config.js @@ -92,6 +92,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 %>)", diff --git a/makeSql.js b/makeSql.js index 84a6fb8..053e5cf 100644 --- a/makeSql.js +++ b/makeSql.js @@ -124,6 +124,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, @@ -136,5 +142,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 9673ca9..024820c 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'}), @@ -123,6 +124,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..084c6bc --- /dev/null +++ b/style/boundaryCategory.mms @@ -0,0 +1,83 @@ +#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; + } + } + + ::fill { + + [category="Parcels"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Zones"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="SID"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Park"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #ED9FA7; + polygon-opacity: 0.2; + } + + [category="Main Neighborhood"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #55A9F2; + polygon-opacity: 0.2; + } + + [category="Neighborhood"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + 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: true; + + [zoom < 14] { text-name: ''; } + [category="Parcels"] { text-name: ''; } + } +} From c79495c43247c25166bf93a06163c667ed81e989 Mon Sep 17 00:00:00 2001 From: Tom Z Date: Sun, 16 Aug 2020 14:51:21 -0400 Subject: [PATCH 4/6] develop - styling for sections not in a category --- style/boundaryCategory.mms | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/style/boundaryCategory.mms b/style/boundaryCategory.mms index 084c6bc..ed08a7f 100644 --- a/style/boundaryCategory.mms +++ b/style/boundaryCategory.mms @@ -14,9 +14,19 @@ line-color:#ddd; line-opacity: 0.7; } + [category="Ward"] { + line-width: 5; + line-color:#ddd; + line-opacity: 0.7; + } } ::fill { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #55A9F2; + polygon-opacity: 0.2; [category="Parcels"] { line-width: 0.5; @@ -65,6 +75,14 @@ polygon-fill: #55A9F2; polygon-opacity: 0.2; } + + [category="Ward"] { + line-width: 0.5; + line-color: #444; + line-dasharray: 10, 8; + polygon-fill: #55A9F2; + polygon-opacity: 0.2; + } } ::label { From aa711b73306e38299ccbf0a336621642fce2f494 Mon Sep 17 00:00:00 2001 From: Tom Z Date: Tue, 17 Aug 2021 22:08:51 -0400 Subject: [PATCH 5/6] develop - fix up the boundaries --- style/boundaryCategory.mms | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/style/boundaryCategory.mms b/style/boundaryCategory.mms index ed08a7f..816c31a 100644 --- a/style/boundaryCategory.mms +++ b/style/boundaryCategory.mms @@ -22,64 +22,43 @@ } ::fill { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; + line-width: 0.75; + line-color: #000; + line-opacity: 0.8; polygon-fill: #55A9F2; polygon-opacity: 0.2; [category="Parcels"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #ED9FA7; polygon-opacity: 0.2; } [category="Zones"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #ED9FA7; polygon-opacity: 0.2; } [category="SID"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #ED9FA7; polygon-opacity: 0.2; } [category="Park"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #ED9FA7; polygon-opacity: 0.2; } [category="Main Neighborhood"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #55A9F2; polygon-opacity: 0.2; } [category="Neighborhood"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; - polygon-fill: #55A9F2; + polygon-fill: #557CF2; polygon-opacity: 0.2; } [category="Ward"] { - line-width: 0.5; - line-color: #444; - line-dasharray: 10, 8; polygon-fill: #55A9F2; polygon-opacity: 0.2; } @@ -93,7 +72,7 @@ text-halo-fill: rgba(255, 255, 255, 0.5); text-halo-radius: 1; text-placement: interior; - text-avoid-edges: true; + text-avoid-edges: false; [zoom < 14] { text-name: ''; } [category="Parcels"] { text-name: ''; } From 9ef46897cb61f58c124b13a0cd0602e03a7f68ca Mon Sep 17 00:00:00 2001 From: Tom Z Date: Wed, 25 Aug 2021 22:43:22 -0400 Subject: [PATCH 6/6] Added a conditions to the mapFeature query so we can color specifically on feature. For now, we are hard-coding the Unhealthy and Dead feature. The feature colors match the colors used in the otm-core dashboard. The Dockerfile was adjusted to force yes on some unsecure node.js 6.x. We still need to properly update node.js in here. --- Dockerfile | 2 +- config.js | 3 ++- makeSql.js | 9 +++++++-- server.js | 4 +++- style/mapFeature.mms | 18 ++++++++++++++++++ 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index d26996d..30a7835 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update && apt-get install -y \ libpango1.0-dev \ curl -RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - && apt-get install -y nodejs +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 diff --git a/config.js b/config.js index a99bf92..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, <%= importerField %> as importer_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" }, diff --git a/makeSql.js b/makeSql.js index 053e5cf..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, showImportedTrees) { + 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) : {}, @@ -87,7 +88,11 @@ function makeSqlForMapFeatures(filterString, displayString, restrictFeatureStrin } else { // we only sometimes want different colors for imported trees var importerField = showImportedTrees ? 'importer_treeimportrow.id' : 'null'; - otherFields = _.template(config.sqlForMapFeatures.fields.base)({'importerField': importerField}); + 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", diff --git a/server.js b/server.js index 024820c..ec1a097 100644 --- a/server.js +++ b/server.js @@ -105,6 +105,7 @@ var windshaftConfig = { isUtfGridRequest = (req.params.format === 'grid.json'); var showImportedTrees = table === 'importer_treerowimport'; + var showTreeCondition = req.query[config.showTreeCondition] === 'true'; req.params.sql = makeSql.makeSqlForMapFeatures( filterString, @@ -115,7 +116,8 @@ var windshaftConfig = { isUtfGridRequest, isPolygonRequest, req.instanceConfig, - showImportedTrees + showImportedTrees, + showTreeCondition ); if (isPolygonRequest) { req.params.style = styles.polygonalMapFeature; diff --git a/style/mapFeature.mms b/style/mapFeature.mms index e147fd6..12449c4 100644 --- a/style/mapFeature.mms +++ b/style/mapFeature.mms @@ -10,6 +10,12 @@ @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; @@ -24,6 +30,12 @@ [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; @@ -43,6 +55,12 @@ [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; }