diff --git a/mapsense.js b/mapsense.js index 862e019..2d4fd97 100644 --- a/mapsense.js +++ b/mapsense.js @@ -1232,7 +1232,17 @@ ms.geoJson = function(fetch) { pointRadius = 4.5, features, tileBackground = false, - selection; + transformData, + key = (function() { + var k = 0; + return function(f) { + k = k + 1 | 0; + return k; + }; + })(), + selection, + dataGeneration = 0, + selectionGeneration = 0; container.setAttribute("fill-rule", "evenodd"); clipPath.setAttribute("id", clipId); @@ -1294,16 +1304,14 @@ ms.geoJson = function(fetch) { function load(tile, proj) { var g = tile.element = ms.svg("g"); - var tileProj = proj(tile); - + tile.proj = proj(tile); + tile.fetched = []; tile.features = []; + tile.draw = function() { + draw(g, tile); + }; function update(data) { - var updated = []; - - /* Fetch the next batch of features, if so directed. */ - if (data.next) tile.request = fetch(data.next.href, update); - if (geoJson.tile() && tileBackground) { var tileSize = geoJson.map().tileSize(); d3.select(g.insertBefore(ms.svg("rect"), g.firstChild)) @@ -1312,11 +1320,11 @@ ms.geoJson = function(fetch) { .attr("class", "tile-background"); } - draw(g, data, tileProj, updated, tile); + tile.fetched = data.features; + tile.draw(); tile.ready = true; - updated.push.apply(tile.features, updated); - geoJson.dispatch({type: "load", tile: tile, features: updated}); + geoJson.dispatch({type: "load", tile: tile, features: tile.features}); } if (url != null) { @@ -1350,15 +1358,15 @@ ms.geoJson = function(fetch) { return points; } - function draw(g, data, tileProj, updated, tile) { - var proj = projection(tileProj.locationPoint); + function draw(g, tile) { + var proj = projection(tile.proj.locationPoint), + path = projectSpherical(tile.proj), + pathFeatures = [], + pointFeatures = [], + features = transformData ? transformData(tile.fetched, tile) : tile.fetched, + updated = []; - var path = projectSpherical(tileProj); - - var pathFeatures = [], - pointFeatures = []; - - data.features.forEach(function(f) { + features.forEach(function(f) { if (f.geometry.type === "Point") pointFeatures.push(projectPoint(f, proj)); else if (f.geometry.type === "MultiPoint") @@ -1369,13 +1377,15 @@ ms.geoJson = function(fetch) { var pathUpdate = d3.select(g) .selectAll("path") - .data(pathFeatures) - .enter() + .data(pathFeatures, key); + + pathUpdate.enter() .append("path") .attr("d", function(f) { return path(f); }); - if (updated) - pathUpdate.each(function(f) { updated.push({ element: this, data: f }); }); + pathUpdate.exit().remove(); + + pathUpdate.each(function(f) { updated.push({ element: this, data: f }); }); var initialScale = ""; if (scale == "fixed") { @@ -1384,21 +1394,27 @@ ms.geoJson = function(fetch) { var pointUpdate = d3.select(g) .selectAll("circle") - .data(pointFeatures) - .enter() + .data(pointFeatures, key); + + pointUpdate.enter() .append("circle") .attr("transform", function(f) { return "translate(" + f.x + "," + f.y + ")" + initialScale; }) .attr("r", pointRadius); - if (updated) - pointUpdate.each(function(f) { updated.push({ element: this, data: f }); }); + pointUpdate.exit().remove(); + + pointUpdate.each(function(f) { updated.push({ element: this, data: f }); }); if (selection) { pathUpdate.push.apply(pathUpdate, pointUpdate); selection(pathUpdate); } + + tile.features = updated; + tile.dataGeneration = dataGeneration; + tile.selectionGeneration = selectionGeneration; } function unload(tile) { @@ -1437,6 +1453,14 @@ ms.geoJson = function(fetch) { geoJson.selection = function(x) { if (!arguments.length) return selection; selection = x; + selectionGeneration = selectionGeneration + 1 | 0; + return geoJson.reshow(); + }; + + geoJson.transformData = function(x) { + if (!arguments.length) return transformData; + transformData = x; + dataGeneration = dataGeneration + 1 | 0; return geoJson.reshow(); }; @@ -1496,10 +1520,13 @@ ms.geoJson = function(fetch) { geoJson.show = function(tile) { if (clip) tile.element.setAttribute("clip-path", clipHref); else tile.element.removeAttribute("clip-path"); - if (selection) { - var s = d3.select(tile.element).selectAll("path"); - s.push.apply(s, d3.select(tile.element).selectAll("circle")); + if (transformData && tile.dataGeneration !== dataGeneration) { + tile.draw(); + } + else if (selection && tile.selectionGeneration !== selectionGeneration) { + var s = d3.select(tile.element).selectAll("path,circle"); selection(s); + tile.selectionGeneration = selectionGeneration; } geoJson.dispatch({type: "show", tile: tile, features: tile.features}); return geoJson; diff --git a/src/GeoJson.js b/src/GeoJson.js index 391ddef..5a32a11 100644 --- a/src/GeoJson.js +++ b/src/GeoJson.js @@ -12,7 +12,17 @@ ms.geoJson = function(fetch) { pointRadius = 4.5, features, tileBackground = false, - selection; + transformData, + key = (function() { + var k = 0; + return function(f) { + k = k + 1 | 0; + return k; + }; + })(), + selection, + dataGeneration = 0, + selectionGeneration = 0; container.setAttribute("fill-rule", "evenodd"); clipPath.setAttribute("id", clipId); @@ -74,16 +84,14 @@ ms.geoJson = function(fetch) { function load(tile, proj) { var g = tile.element = ms.svg("g"); - var tileProj = proj(tile); - + tile.proj = proj(tile); + tile.fetched = []; tile.features = []; + tile.draw = function() { + draw(g, tile); + }; function update(data) { - var updated = []; - - /* Fetch the next batch of features, if so directed. */ - if (data.next) tile.request = fetch(data.next.href, update); - if (geoJson.tile() && tileBackground) { var tileSize = geoJson.map().tileSize(); d3.select(g.insertBefore(ms.svg("rect"), g.firstChild)) @@ -92,11 +100,11 @@ ms.geoJson = function(fetch) { .attr("class", "tile-background"); } - draw(g, data, tileProj, updated, tile); + tile.fetched = data.features; + tile.draw(); tile.ready = true; - updated.push.apply(tile.features, updated); - geoJson.dispatch({type: "load", tile: tile, features: updated}); + geoJson.dispatch({type: "load", tile: tile, features: tile.features}); } if (url != null) { @@ -130,15 +138,15 @@ ms.geoJson = function(fetch) { return points; } - function draw(g, data, tileProj, updated, tile) { - var proj = projection(tileProj.locationPoint); + function draw(g, tile) { + var proj = projection(tile.proj.locationPoint), + path = projectSpherical(tile.proj), + pathFeatures = [], + pointFeatures = [], + features = transformData ? transformData(tile.fetched, tile) : tile.fetched, + updated = []; - var path = projectSpherical(tileProj); - - var pathFeatures = [], - pointFeatures = []; - - data.features.forEach(function(f) { + features.forEach(function(f) { if (f.geometry.type === "Point") pointFeatures.push(projectPoint(f, proj)); else if (f.geometry.type === "MultiPoint") @@ -149,13 +157,15 @@ ms.geoJson = function(fetch) { var pathUpdate = d3.select(g) .selectAll("path") - .data(pathFeatures) - .enter() + .data(pathFeatures, key); + + pathUpdate.enter() .append("path") .attr("d", function(f) { return path(f); }); - if (updated) - pathUpdate.each(function(f) { updated.push({ element: this, data: f }); }); + pathUpdate.exit().remove(); + + pathUpdate.each(function(f) { updated.push({ element: this, data: f }); }); var initialScale = ""; if (scale == "fixed") { @@ -164,21 +174,27 @@ ms.geoJson = function(fetch) { var pointUpdate = d3.select(g) .selectAll("circle") - .data(pointFeatures) - .enter() + .data(pointFeatures, key); + + pointUpdate.enter() .append("circle") .attr("transform", function(f) { return "translate(" + f.x + "," + f.y + ")" + initialScale; }) .attr("r", pointRadius); - if (updated) - pointUpdate.each(function(f) { updated.push({ element: this, data: f }); }); + pointUpdate.exit().remove(); + + pointUpdate.each(function(f) { updated.push({ element: this, data: f }); }); if (selection) { pathUpdate.push.apply(pathUpdate, pointUpdate); selection(pathUpdate); } + + tile.features = updated; + tile.dataGeneration = dataGeneration; + tile.selectionGeneration = selectionGeneration; } function unload(tile) { @@ -217,6 +233,14 @@ ms.geoJson = function(fetch) { geoJson.selection = function(x) { if (!arguments.length) return selection; selection = x; + selectionGeneration = selectionGeneration + 1 | 0; + return geoJson.reshow(); + }; + + geoJson.transformData = function(x) { + if (!arguments.length) return transformData; + transformData = x; + dataGeneration = dataGeneration + 1 | 0; return geoJson.reshow(); }; @@ -276,10 +300,13 @@ ms.geoJson = function(fetch) { geoJson.show = function(tile) { if (clip) tile.element.setAttribute("clip-path", clipHref); else tile.element.removeAttribute("clip-path"); - if (selection) { - var s = d3.select(tile.element).selectAll("path"); - s.push.apply(s, d3.select(tile.element).selectAll("circle")); + if (transformData && tile.dataGeneration !== dataGeneration) { + tile.draw(); + } + else if (selection && tile.selectionGeneration !== selectionGeneration) { + var s = d3.select(tile.element).selectAll("path,circle"); selection(s); + tile.selectionGeneration = selectionGeneration; } geoJson.dispatch({type: "show", tile: tile, features: tile.features}); return geoJson; diff --git a/styles/azure.less b/styles/azure.less new file mode 100644 index 0000000..97ad26f --- /dev/null +++ b/styles/azure.less @@ -0,0 +1,105 @@ +.mapsense-azure { + &.labels { + font-size: 14; + fill: #777; + font-weight: 600; + text-transform: uppercase; + stroke-width: .3; + font-stretch: expanded; + letter-spacing: 1.5; + font-family: "Josefin Sans"; + } + + &.tile-background { + fill: #CBE6F3; + } + &.land { + fill: rgb(255, 255, 255); + stroke: rgb(162, 211, 242); + stroke-width: 1; + } + &.water_polygon { + fill: #CBE6F3; + stroke: #b6d3e0; + stroke-width: 0.5px; + } + &.water_line { + stroke: rgb(162, 211, 242); + } + &.park { + fill: none; + stroke-width: 0; + stroke: rgb(218, 216, 216); + } + &.landuse { + fill: none; + stroke: none; + } + &.building { + fill: none; + stroke: rgb(212, 212, 212); + stroke-width: .52; + } + &.school { + fill: none; + stroke: none; + } + &.other { + fill: none; + stroke: none; + } + &.urban { + fill: none; + stroke: none; + } + + &.ne_10m_roads { + stroke: #eee; + } + &.motorway { + stroke: #ddd; + } + &.arterial_major { + stroke: #eee; + } + &.arterial_minor { + stroke: #eee; + } + &.road_med { + stroke: #eee; + } + &.road_minor { + stroke: rgb(245, 245, 245); + } + &.rail_major { + stroke: #ddd; + } + &.rail_minor { + stroke: #ddd; + } + &.runway { + stroke: #ddd; + } + &.path { + stroke: rgb(245, 245, 245); + } + &.country_border, &.disputed_border { + stroke: #ccc; + } + &.state_border { + stroke: #ccc; + } + &.ne_10m_roads._3 { + stroke-width: 0.5; + stroke: none; + } + &.ne_10m_roads._4, + &.ne_10m_roads._5 { + stroke-width: 0.75; + stroke: none; + } + &.ne_10m_roads._6 { + opacity: 0.75; + stroke: none; + } +} diff --git a/styles/mapsense.less b/styles/mapsense.less index f132184..31e45f6 100644 --- a/styles/mapsense.less +++ b/styles/mapsense.less @@ -9,6 +9,8 @@ @import "sketch"; @import "tron"; @import "vintage"; +@import "azure"; +@import "pastel"; svg.mapsense-map { width: 100%; diff --git a/styles/pastel.less b/styles/pastel.less new file mode 100644 index 0000000..dfa6190 --- /dev/null +++ b/styles/pastel.less @@ -0,0 +1,104 @@ +.mapsense-pastel { + &.labels { + font-size: 14; + fill: #777; + font-weight: 600; + text-transform: uppercase; + stroke-width: .3; + font-stretch: expanded; + letter-spacing: 1.5; + font-family: "Josefin Sans"; + } + + &.tile-background { + fill: #CBE6F3; + } + &.land { + fill: rgb(255, 255, 255); + stroke: rgb(162, 211, 242); + stroke-width: 1; + } + &.water_polygon { + fill: #CBE6F3; + stroke: #b6d3e0; + stroke-width: 0.5px; + } + &.water_line { + stroke: rgb(162, 211, 242); + } + &.park { + fill: #c6f3bd; + stroke: none; + } + &.landuse { + fill: none; + stroke: none; + } + &.building { + fill: none; + stroke: rgb(212, 212, 212); + stroke-width: .52; + } + &.school { + fill: none; + stroke: none; + } + &.other { + fill: none; + stroke: none; + } + &.urban { + fill: rgba(243, 210, 191, 0.19); + stroke: none; + } + + &.ne_10m_roads { + stroke: #eee; + } + &.motorway { + stroke: #ddd; + } + &.arterial_major { + stroke: #eee; + } + &.arterial_minor { + stroke: #eee; + } + &.road_med { + stroke: #eee; + } + &.road_minor { + stroke: rgb(245, 245, 245); + } + &.rail_major { + stroke: #ddd; + } + &.rail_minor { + stroke: #ddd; + } + &.runway { + stroke: #ddd; + } + &.path { + stroke: rgb(245, 245, 245); + } + &.country_border, &.disputed_border { + stroke: #ccc; + } + &.state_border { + stroke: #ccc; + } + &.ne_10m_roads._3 { + stroke-width: 0.5; + stroke: none; + } + &.ne_10m_roads._4, + &.ne_10m_roads._5 { + stroke-width: 0.75; + stroke: none; + } + &.ne_10m_roads._6 { + opacity: 0.75; + stroke: none; + } +} diff --git a/styles/simple.less b/styles/simple.less new file mode 100644 index 0000000..087ca37 --- /dev/null +++ b/styles/simple.less @@ -0,0 +1,105 @@ +.mapsense-simple { + &.labels { + font-size: 14; + fill: #777; + font-weight: 600; + text-transform: uppercase; + stroke-width: .3; + font-stretch: expanded; + letter-spacing: 1.5; + font-family: "Josefin Sans"; + } + + &.tile-background { + fill: #CBE6F3; + } + &.land { + fill: rgb(255, 255, 255); + stroke: rgb(162, 211, 242); + stroke-width: 1; + } + &.water_polygon { + fill: #CBE6F3; + stroke: #b6d3e0; + stroke-width: 0.5px; + } + &.water_line { + stroke: rgb(162, 211, 242); + } + &.park { + fill: none; + stroke-width: 0; + stroke: rgb(218, 216, 216); + } + &.landuse { + fill: none; + stroke: none; + } + &.building { + fill: none; + stroke: rgb(212, 212, 212); + stroke-width: .52; + } + &.school { + fill: none; + stroke: none; + } + &.other { + fill: none; + stroke: none; + } + &.urban { + fill: none; + stroke: none; + } + + &.ne_10m_roads { + stroke: #eee; + } + &.motorway { + stroke: #ddd; + } + &.arterial_major { + stroke: #eee; + } + &.arterial_minor { + stroke: #eee; + } + &.road_med { + stroke: #eee; + } + &.road_minor { + stroke: rgb(245, 245, 245); + } + &.rail_major { + stroke: #ddd; + } + &.rail_minor { + stroke: #ddd; + } + &.runway { + stroke: #ddd; + } + &.path { + stroke: rgb(245, 245, 245); + } + &.country_border, &.disputed_border { + stroke: #ccc; + } + &.state_border { + stroke: #ccc; + } + &.ne_10m_roads._3 { + stroke-width: 0.5; + stroke: none; + } + &.ne_10m_roads._4, + &.ne_10m_roads._5 { + stroke-width: 0.75; + stroke: none; + } + &.ne_10m_roads._6 { + opacity: 0.75; + stroke: none; + } +}