├── .Rbuildignore ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── R └── mapshaper.R ├── README.md ├── build.R ├── examples ├── example_mapshape.R └── example_shiny.R ├── images └── screencast.gif ├── inst └── htmlwidgets │ ├── lib │ └── mapshaper │ │ ├── LICENSE │ │ ├── README.md │ │ ├── elements.css │ │ ├── images │ │ ├── close.png │ │ ├── fork-me-right-cerulean@2x.png │ │ ├── fork-me-right-graphite@2x.png │ │ ├── home.png │ │ ├── slider_handle_v1.png │ │ ├── zoomin.png │ │ └── zoomout.png │ │ ├── mapshaper.js │ │ └── page.css │ ├── mapshaper.js │ └── mapshaper.yaml ├── man ├── mapshaper.Rd ├── mapshaperOutput.Rd └── renderMapshaper.Rd └── mapshaper_htmlwidget.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | build.R 4 | examples 5 | images 6 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: mapshaperWidget 2 | Type: Package 3 | Title: R Htmlwidget for Mapshaper 4 | Version: 0.1 5 | Date: 2015-08-09 6 | Authors@R: c( 7 | person( 8 | "Matthew", "Bloch" 9 | , role = c("aut", "cph") 10 | , comment = "mapshaper code in htmlwidgets/lib, https://github.com/mbloch/mapshaper" 11 | ), 12 | person( 13 | "Mike", "Bostock" 14 | , role = c("aut", "cph") 15 | , comment = "d3.js library in htmlwidgets/lib, http://d3js.org" 16 | ), 17 | person( 18 | "Kent", "Russell" 19 | , role = c("aut", "cre") 20 | , comment = "R interface" 21 | , email = "kent.russell@timelyportfolio.com" 22 | ) 23 | ) 24 | Maintainer: Kent Russell 25 | Description: Use mapshaper in R as an htmlwidget for a more seamless experience. 26 | License: MIT + file LICENSE 27 | Imports: 28 | geojsonio, 29 | htmltools 30 | LazyData: TRUE 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2 (4.1.0): do not edit by hand 2 | 3 | export(mapshaper) 4 | export(mapshaperOutput) 5 | export(renderMapshaper) 6 | import(geojsonio) 7 | import(htmlwidgets) 8 | -------------------------------------------------------------------------------- /R/mapshaper.R: -------------------------------------------------------------------------------- 1 | #' Mapshaper in R 2 | #' 3 | #' Use \href{www.mapshaper.org}{mapshaper} within R to analyze and simplify 4 | #' your spatial objects. 5 | #' 6 | #' @param ... One or many input lists, data.frame, or spatial class. If \code{input} 7 | #' is not \code{json}, then automatic conversion will be attempted 8 | #' with \code{\link[geojsonio]{geojson_json}}. 9 | #' 10 | #' @example examples/example_mapshape.R 11 | #' 12 | #' @import htmlwidgets geojsonio 13 | #' 14 | #' @export 15 | mapshaper <- function( ..., width = "100%", height = "100%") { 16 | input <- lazyeval::auto_name(lazyeval::lazy_dots(...)) 17 | 18 | if(!is.null(input)){ 19 | files <- lapply( 20 | names(input) 21 | ,function(name){ 22 | x <- lazyeval::lazy_eval(input[[name]]) 23 | 24 | x <- x 25 | 26 | if(!inherits(x,c("character","json","list"))) { 27 | # attempt conversion with geojsonio 28 | x <- geojson_json( x ) 29 | } 30 | 31 | if(inherits(x,"json")){ 32 | x <- as.character(x) 33 | } 34 | 35 | list( 36 | name = name 37 | ,geojson = x 38 | ) 39 | } 40 | ) 41 | } 42 | 43 | # forward options using x 44 | x = list( 45 | files = files 46 | ) 47 | 48 | # create widget 49 | htmlwidgets::createWidget( 50 | name = 'mapshaper', 51 | x, 52 | width = width, 53 | height = height, 54 | package = 'mapshaperWidget' 55 | ) 56 | } 57 | 58 | #' Widget output function for use in Shiny 59 | #' 60 | #' @export 61 | mapshaperOutput <- function(outputId, width = '100%', height = '400px'){ 62 | shinyWidgetOutput(outputId, 'mapshaper', width, height, package = 'mapshaperWidget') 63 | } 64 | 65 | #' Widget render function for use in Shiny 66 | #' 67 | #' @export 68 | renderMapshaper <- function(expr, env = parent.frame(), quoted = FALSE) { 69 | if (!quoted) { expr <- substitute(expr) } # force quoted 70 | shinyRenderWidget(expr, mapshaperOutput, env, quoted = TRUE) 71 | } 72 | 73 | 74 | # custom mapshaper html 75 | mapshaper_html <- function(name, package, id, style, class, ...){ 76 | htmltools::tags$div(id = id, style = style, style = "position:relative;", class = class, ... 77 | , 78 | htmltools::HTML(' 79 | 115 | 116 |
117 |
118 | 134 | 135 |
136 |
137 |

Unfortunately, mapshaper can\'t run in this web browser

138 |
For best results, try Google Chrome or Mozilla Firefox.
139 |
140 |
141 | 142 | 151 | 152 | 171 | 172 | 216 | 217 | 265 | 266 | 267 |
268 |
269 |
270 |
271 |
272 |
273 |
0 line intersections
274 |
Repair
275 |
276 |
277 |
278 |
279 | ' 280 | ) 281 | ) 282 | } 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R `htmlwidget` for mapshaper 2 | 3 | [mapshaper](https://github.com/mbloch/mapshaper) is an incredibly powerful set of tools to simplify and edit maps from `Shapefile`, `geoJSON`, `TopoJSON`, and other file formats. This `htmlwidget` brings `mapshaper` to your `R` experience. 4 | 5 | ```r 6 | #devtools::install_github("timelyportfolio/mapshaper_htmlwidget") 7 | 8 | library(mapshaperWidget) 9 | library(tmap) 10 | library(sp) 11 | 12 | data(World) 13 | plot(World) 14 | 15 | mapshaper(World) 16 | ``` 17 | 18 | ![screenshot of mapshaper](images/screencast.gif) 19 | 20 | ## To-do 21 | 22 | - Hook it up to `Shiny` so the export result is returned to `R` or the hover info is provided to `R`. 23 | - Separate JS dependencies, namespace css, and isolate components so that it will play nicely with other `htmlwidgets` 24 | -------------------------------------------------------------------------------- /build.R: -------------------------------------------------------------------------------- 1 | ## convert images in mapshaper.js to base64 2 | ## the relative url will not work since added in JavaScript 3 | 4 | library(base64enc) 5 | 6 | close_png <- base64enc::dataURI( 7 | file="inst/htmlwidgets/lib/mapshaper/images/close.png" 8 | ,mime = "image/png" 9 | ) 10 | 11 | 12 | slider_png <- base64enc::dataURI( 13 | file="inst/htmlwidgets/lib/mapshaper/images/slider_handle_v1.png" 14 | ,mime = "image/png" 15 | ) 16 | 17 | mapshape_js <- readLines( "inst/htmlwidgets/lib/mapshaper/mapshaper.js" ) 18 | 19 | mapshape_js <- gsub( 20 | x = mapshape_js 21 | , pattern = "(images/close.png)" 22 | , replacement = close_png 23 | ) 24 | 25 | cat(mapshape_js,file="inst/htmlwidgets/lib/mapshaper/mapshaper.js",sep="\n") 26 | 27 | #### remove position relative in css for the main page 28 | page_css <- readLines( "inst/htmlwidgets/lib/mapshaper/page.css" ) 29 | 30 | page_mshp_line <- which(grepl( 31 | x = page_css 32 | , pattern = "(#mshp-main-page \\{)" 33 | )) 34 | 35 | page_css[page_mshp_line+1] <- "/*position:relative*/" 36 | 37 | cat(page_css,file = "inst/htmlwidgets/lib/mapshaper/page.css",sep="\n") 38 | -------------------------------------------------------------------------------- /examples/example_mapshape.R: -------------------------------------------------------------------------------- 1 | library('geojsonio') 2 | library('sp') 3 | library('mapshaperWidget') 4 | 5 | ### straight from geojson 6 | alabama <- paste0( 7 | readLines( 8 | "https://raw.githubusercontent.com/glynnbird/usstatesgeojson/master/alabama.geojson" 9 | ) 10 | ,collapse = " " 11 | ) 12 | mapshaper(alabama) 13 | 14 | ### example from geojsonio vignette 15 | poly1 <- Polygons( 16 | list( 17 | Polygon(cbind(c(-100,-90,-85,-100), c(40,50,45,40))) 18 | ) 19 | , "1" 20 | ) 21 | poly2 <- Polygons( 22 | list( 23 | Polygon(cbind(c(-90,-80,-75,-90),c(30,40,35,30))) 24 | ) 25 | , "2" 26 | ) 27 | sp_poly <- SpatialPolygons(list(poly1, poly2), 1:2) 28 | 29 | mapshaper( sp_poly ) 30 | 31 | 32 | ### how to provide multiple spatial to mapshaper 33 | calif <- geojson_read( 34 | system.file("examples", "california.geojson", package = "geojsonio") 35 | ) 36 | mapshaper( sp_poly, calif ) 37 | -------------------------------------------------------------------------------- /examples/example_shiny.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(leaflet) 3 | library(geojsonio) 4 | library(mapshaperWidget) 5 | 6 | calif <- geojson_read( 7 | system.file("examples/california.geojson",package="geojsonio") 8 | ) 9 | 10 | ui <- fluidPage( 11 | fluidRow( 12 | column( 8, mapshaperOutput("mapshaper1", height = 600) ) 13 | ,column( 4, leafletOutput( "leafmap", height = 600 )) 14 | ) 15 | ,fluidRow( 16 | textOutput("message", container = p) 17 | ) 18 | ) 19 | 20 | server <- function(input, output, session) { 21 | v <- reactiveValues(msg = "") 22 | 23 | output$mapshaper1 <- renderMapshaper({ 24 | mapshaper( calif ) 25 | }) 26 | 27 | output$leafmap <- renderLeaflet({ 28 | leaflet( ) %>% 29 | addGeoJSON( calif ) %>% 30 | fitBounds( -124, 32, -113, 43 ) 31 | }) 32 | 33 | observeEvent(input$mapshaper1_export, { 34 | v$msg <- paste("Exported", input$mapshaper1_export$content) 35 | output$leafmap <- renderLeaflet( 36 | leaflet( ) %>% 37 | addGeoJSON( input$mapshaper1_export$content ) %>% 38 | fitBounds( -124, 32, -113, 43 ) 39 | ) 40 | }) 41 | 42 | output$message <- renderText(v$msg) 43 | } 44 | 45 | shinyApp(ui, server) 46 | -------------------------------------------------------------------------------- /images/screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/images/screencast.gif -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Matthew Bloch 2 | 3 | This Source Code Form is subject to the terms of the Mozilla Public 4 | License, v. 2.0. If a copy of the MPL was not distributed with this 5 | file, You can obtain one at http://mozilla.org/MPL/2.0/. -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/README.md: -------------------------------------------------------------------------------- 1 | # mapshaper 2 | 3 | ### Introduction 4 | 5 | Mapshaper is software for editing Shapefile, GeoJSON, [TopoJSON](https://github.com/mbostock/topojson/wiki) and several other data formats, written in JavaScript. 6 | 7 | The `mapshaper` command line program supports common map making tasks like simplifying shapes, editing attribute data, clipping, erasing, dissolving, filtering and more. 8 | 9 | There is a [web UI](http://www.mapshaper.org) that supports interactive simplification and attribute editing. The web interface also has as a built in console for running cli commands. 10 | 11 | See the [project wiki](https://github.com/mbloch/mapshaper/wiki) for documentation on using mapshaper. 12 | 13 | To suggest improvements, add an [issue](https://github.com/mbloch/mapshaper/issues). 14 | 15 | To learn about recent updates, read the [changelog](https://github.com/mbloch/mapshaper/releases). 16 | 17 | ### Command line tool 18 | 19 | The `mapshaper` command line program has been used successfully under Mac OS X, Linux and Windows. 20 | 21 | The project wiki has an [introduction](https://github.com/mbloch/mapshaper/wiki/Introduction-to-the-Command-Line-Tool) to using the command line tool that includes many simple examples. 22 | 23 | For a detailed reference, see the [Command Reference](https://github.com/mbloch/mapshaper/wiki/Command-Reference). 24 | 25 | ### Interactive tool 26 | 27 | The web UI works in recent versions of Chrome and Firefox as well as IE 10+. Exporting is not supported in Safari. If you encounter out-of-memory errors using Chrome, try Firefox, which can handle Shapefiles larger than 1GB. 28 | 29 | The mapshaper distribution includes the script `mapshaper-gui`, which runs mapshaper's web interface locally. You can also visit [mapshaper.org](http://www.mapshaper.org) to use mapshaper online. 30 | 31 | All processing is done in the browser, so your data stays private, even when using the public website. 32 | 33 | ### Installation 34 | 35 | Mapshaper requires [Node.js](http://nodejs.org/download/) or [io.js](https://iojs.org). 36 | 37 | With Node or io.js installed, you can install the latest release version of mapshaper using npm. Install with the "-g" flag to make the executable scripts available systemwide. 38 | 39 | ```bash 40 | npm install -g mapshaper 41 | ``` 42 | 43 | To install and run the latest development code from github: 44 | 45 | ```bash 46 | git clone git@github.com:mbloch/mapshaper.git 47 | cd mapshaper 48 | npm install 49 | bin/mapshaper-gui # use the web UI locally 50 | bin/mapshaper # run the command line program 51 | ``` 52 | 53 | ### Building and testing 54 | 55 | Run the `build` script after editing any of the files in the src/ or lib/ directories. 56 | 57 | Run `npm test` in the project directory to run mapshaper's tests. 58 | 59 | ### License 60 | 61 | This software is licensed under [MPL 2.0](http://www.mozilla.org/MPL/2.0/). 62 | 63 | According to Mozilla's [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html), "The MPL's ‘file-level’ copyleft is designed to encourage contributors to share modifications they make to your code, while still allowing them to combine your code with code under other licenses (open or proprietary) with minimal restrictions." 64 | 65 | ### Acknowledgements 66 | 67 | Gregor Aisch, Mike Bostock, Shan Carter and Zhou Yi, for suggesting improvements and general helpfulness. 68 | 69 | Mark Harrower, for collaborating on the original MapShaper program at the University of Wisconsin–Madison. 70 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/elements.css: -------------------------------------------------------------------------------- 1 | 2 | /* Hide Firefox's indicator when images are loading */ 3 | img:-moz-loading { 4 | visibility: hidden; 5 | } 6 | 7 | /* ------ UTILITY ---------- */ 8 | 9 | .hidden { 10 | display: none; 11 | } 12 | 13 | div.tip { 14 | color: #555; 15 | font-weight: normal; 16 | text-align: left; 17 | position: relative; 18 | display: inline-block; 19 | font-size: 13px; 20 | line-height: 1.45; 21 | background: #fff; 22 | border: 1px solid #a2a2a2; 23 | border-radius: 6px; 24 | padding: 8px 11px 9px 11px; 25 | bottom: 0px; 26 | left: -50%; 27 | margin-right: 9px; 28 | white-space: pre; 29 | box-shadow: 0 0px 10px rgba(0, 0, 0, 0.15); 30 | } 31 | 32 | /* tail */ 33 | .tip:after, .tip:before { 34 | top: 100%; 35 | border: solid transparent; 36 | content: " "; 37 | height: 0; 38 | width: 0; 39 | position: absolute; 40 | pointer-events: none; 41 | } 42 | 43 | /* tail fg */ 44 | .tip:after { 45 | border-color: rgba(136, 183, 213, 0); 46 | border-top-color: #fff; 47 | border-width: 8px; 48 | left: 50%; 49 | margin-left: -8px; 50 | } 51 | 52 | /* tail bg */ 53 | .tip:before { 54 | border-color: rgba(194, 225, 245, 0); 55 | border-top-color: #999; 56 | border-width: 9px; 57 | left: 50%; 58 | margin-left: -9px; 59 | } 60 | 61 | 62 | .tip-button { 63 | float: right; 64 | margin-top: 2px; 65 | display:inline-block; 66 | position: relative; 67 | z-index: 500; 68 | cursor: pointer; 69 | text-align: center; 70 | font-size: 13px; 71 | line-height: 1; 72 | font-weight: normal; 73 | color: #799FCB; 74 | } 75 | 76 | .tip-button .tip-anchor { 77 | bottom: 24px; 78 | left: 8px; 79 | } 80 | 81 | .tip-anchor { 82 | visibility: hidden; 83 | position: absolute; 84 | bottom: 0; 85 | left: 0; 86 | } 87 | 88 | .tip-button:hover { 89 | font-weight: bold; 90 | color: #033D6D; 91 | } 92 | 93 | .tip-button:hover * { 94 | visibility: visible; 95 | } 96 | 97 | .clicktext { 98 | display:inline-block; 99 | line-height: 1; 100 | cursor: pointer; 101 | border: none; 102 | outline: none; 103 | padding: 0; 104 | margin: 0; 105 | text-decoration:none; 106 | text-indent: 0px; 107 | } 108 | 109 | .file-control { 110 | position: absolute; 111 | top: -1000px; 112 | } 113 | 114 | /* -------- BUTTONS ---------- */ 115 | 116 | .mode-btn { 117 | margin: 5px 0 0 0; 118 | color: #fff; 119 | border: none; 120 | padding: 3px 7px 4px 7px; 121 | border-radius: 3px; 122 | } 123 | 124 | .mode-btn:hover { 125 | background-color: #0d546d; 126 | } 127 | 128 | .mode-btn.disabled, 129 | .mode-btn.disabled:hover { 130 | background-color: none; 131 | } 132 | 133 | .mode-btn.active, 134 | .mode-btn.active:hover { 135 | background-color: black; 136 | } 137 | 138 | 139 | .btn.active { 140 | cursor: pointer; 141 | } 142 | 143 | .btn.selected, 144 | .btn.selected:hover { 145 | color: #aaa; 146 | background: none; 147 | } 148 | 149 | .btn.inline-btn:hover:not(.selected) { 150 | background-color: #FFFCDC; 151 | } 152 | 153 | .btn { 154 | text-align: center; 155 | padding: 4px 7px 5px 7px; 156 | border-radius: 4px; 157 | line-height: 1; 158 | display: inline-block; 159 | cursor: pointer; 160 | } 161 | 162 | .btn.disabled { 163 | cursor: default; 164 | } 165 | 166 | 167 | .dialog-btn { 168 | display: inline-block; 169 | margin-bottom: 3px; 170 | margin-top: 2px; 171 | font-size: 13px; 172 | color: white; 173 | min-width: 30px; 174 | background-color: #18799b; 175 | } 176 | 177 | .dialog-btn:hover { 178 | background-color: #0d546d; 179 | } 180 | 181 | .inline-btn { 182 | background-color: white; 183 | text: black; 184 | margin: 0; 185 | border: 1px solid #999; 186 | padding: 1px 4px 3px 3px; 187 | } 188 | 189 | .text-btn { 190 | cursor: pointer; 191 | } 192 | 193 | .text-btn.disabled { 194 | cursor: auto; 195 | color: #999; 196 | } 197 | 198 | 199 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/close.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/fork-me-right-cerulean@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/fork-me-right-cerulean@2x.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/fork-me-right-graphite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/fork-me-right-graphite@2x.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/home.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/slider_handle_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/slider_handle_v1.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/zoomin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/zoomin.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/images/zoomout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/mapshaper_htmlwidget/40608914c777988887d8b1e616acf24f9a7a634f/inst/htmlwidgets/lib/mapshaper/images/zoomout.png -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/mapshaper/page.css: -------------------------------------------------------------------------------- 1 | 2 | .mapshaper { 3 | background-color: #fff; 4 | font: 14px/1.4; 5 | color: #444; 6 | } 7 | 8 | 9 | /* --- Color themes -------------- */ 10 | 11 | .colored-text { 12 | color: #1e6e87; 13 | } 14 | 15 | .dot-underline { 16 | border-bottom: 1px dotted #1e6e87; 17 | } 18 | 19 | /* disabling themes (need to update button col, etc) 20 | /* 21 | .theme1 .dot-underline { 22 | border-bottom: 1px dotted #264372; 23 | } 24 | 25 | .theme1 .colored-text { 26 | color: #264372; 27 | } 28 | 29 | .theme1 .page-header { 30 | background-color: #6E90BA; 31 | background: linear-gradient(to bottom, #6E90BA, #85B3DD); 32 | } 33 | 34 | .theme1 .nav-btn:hover polygon { 35 | fill: #35517c; 36 | } 37 | 38 | .theme1 #fork-me { 39 | background-image: url("images/fork-me-right-cerulean@2x.png"); 40 | } 41 | 42 | .theme1 .dialog-btn { 43 | background-color: #5d85b5; 44 | } 45 | 46 | .theme1 .dialog-btn:hover { 47 | background-color: #35517c; 48 | } 49 | 50 | .theme1 .mode-btn:hover { 51 | background-color: #517293; 52 | } 53 | 54 | 55 | .theme2 .page-header, 56 | .theme2 .dialog-btn { 57 | background-color: #915050; 58 | } 59 | 60 | .theme2 .mode-btn:hover, 61 | .theme2 .dialog-btn:hover { 62 | background-color: #843030; 63 | } 64 | 65 | .theme2 .colored-text { 66 | color: #915050; 67 | } 68 | 69 | 70 | .theme2 .nav-btn polygon { 71 | fill: #915050; 72 | } 73 | 74 | .theme3 .page-header, 75 | .theme3 .dialog-btn { 76 | background-color: #968160; 77 | } 78 | 79 | .theme3 .mode-btn:hover, 80 | .theme3 .dialog-btn:hover { 81 | background-color: #7c674c; 82 | } 83 | 84 | .theme3 .colored-text { 85 | color: #968160; 86 | } 87 | 88 | .theme3 .nav-btn polygon { 89 | fill: #968160; 90 | } 91 | */ 92 | 93 | /* --- Page header --------------- */ 94 | 95 | .page-header { 96 | position: absolute; 97 | color: white; 98 | top: 0; 99 | left: 0; 100 | z-index: 40; 101 | width: 100%; 102 | height: 32px; 103 | background-color: #18799b; 104 | } 105 | 106 | #mshp-version { 107 | z-index: 1; 108 | position: absolute; 109 | pointer-events: none; 110 | bottom: 8px; 111 | left: 11px; 112 | font-size: 11px; 113 | } 114 | 115 | .mapshaper-logo { 116 | font-weight: bold; 117 | font-size: 18px; 118 | margin: 2px 0 0 12px; 119 | } 120 | 121 | .mapshaper-logo .logo-highlight { 122 | color: #ffa; 123 | } 124 | 125 | .page-header a { 126 | text-decoration: none; 127 | } 128 | 129 | #mode-buttons { 130 | z-index: 20; 131 | position: absolute; 132 | top: 0px; 133 | right: 0px; 134 | display: none; 135 | margin: 0 9px 3px 0; 136 | } 137 | 138 | .separator { 139 | border-left: 1px solid white; 140 | height: 10px; 141 | margin: 0 2px 0 2px; 142 | } 143 | 144 | #export-buttons { 145 | display: none; 146 | } 147 | 148 | 149 | /* --- Main area ------------- */ 150 | 151 | .main-area { 152 | position: absolute; 153 | top: 0; 154 | right: 0; 155 | bottom: 0; 156 | left: 0; 157 | margin: 32px 0 0 0; 158 | } 159 | 160 | 161 | /* --- Error message ---------- */ 162 | 163 | .error-wrapper { 164 | z-index: 120; 165 | text-align: center; 166 | position: absolute; 167 | top: 0; 168 | right: 0; 169 | bottom: 0; 170 | left: 0; 171 | } 172 | 173 | div.error-box { 174 | margin-top: 55px; 175 | /* padding: 8px 12px 10px 12px; */ 176 | max-width: 400px; 177 | font-size: 16px; 178 | } 179 | 180 | div.error-box > div { 181 | 182 | } 183 | 184 | 185 | 186 | /* --- Import screen -------------- */ 187 | 188 | #fork-me { 189 | background-image: url("images/fork-me-right-graphite@2x.png"); 190 | background-size: 149px 149px; 191 | position: absolute; 192 | z-index: 110; 193 | top: 0; 194 | right: 0; 195 | border: 0; 196 | width: 149px; 197 | height: 149px; 198 | } 199 | 200 | .option-menu .advanced-options { 201 | width: 210px; 202 | background: rgba(255, 255, 255, 0.4); 203 | border: 1px solid #999; 204 | padding: 0 3px; 205 | height: 17px; 206 | font-size: 12px; 207 | } 208 | 209 | #export-options .option-menu .advanced-options { 210 | width: 225px; 211 | } 212 | 213 | #mshp-not-supported { 214 | display: none; 215 | z-index: 100; 216 | text-align: center; 217 | } 218 | 219 | 220 | #dropped-file-list { 221 | display: none; 222 | padding-bottom: 3px; 223 | } 224 | 225 | 226 | #dropped-file-list p { 227 | line-height: 1; 228 | margin-bottom: 5px; 229 | } 230 | 231 | 232 | .popup-dialog { 233 | display: none; 234 | z-index: 80; 235 | text-align: center; 236 | } 237 | 238 | .info-box { 239 | text-align: left; 240 | margin-top: 12px; 241 | background-color: #fff; 242 | padding: 10px 14px 8px 14px; 243 | vertical-align: top; 244 | display: inline-block; 245 | border: 1px solid #999; 246 | border-radius: 9px; 247 | } 248 | 249 | .info-box h3 { 250 | padding: 0; 251 | margin: 0 0 3px 0; 252 | } 253 | 254 | .info-box h4 { 255 | margin: 0 0 2px 0; 256 | } 257 | 258 | .info-box p { 259 | white-space: pre-line; 260 | margin: 0 0 0.45em 0; 261 | } 262 | 263 | .option-menu { 264 | margin: 0 0 6px 0; 265 | } 266 | 267 | .option-menu input { 268 | width: 12px; 269 | height: 12px; 270 | } 271 | 272 | .option-menu .tip-button { 273 | margin-left: 12px; 274 | margin-top: 3px; 275 | } 276 | 277 | .option-menu input.checkbox { 278 | margin: 3px 5px 0 0; 279 | } 280 | 281 | .option-menu input.radio { 282 | margin: 0 5px 0 0; 283 | } 284 | 285 | /* --- Progress bar --------------- */ 286 | 287 | #progress-message { 288 | position: absolute; 289 | width: 100%; 290 | z-index: 90; 291 | text-align: center; 292 | top: 52px; 293 | } 294 | 295 | #progress-message > div { 296 | display: inline-block; 297 | text-transform: uppercase; 298 | padding: 3px 6px; 299 | font-size: 15px; 300 | color: black; 301 | background-color: rgba(255, 255, 222, 0.85); 302 | border: 1px solid #333; 303 | } 304 | 305 | /* === Editing interface ========== */ 306 | 307 | #mshp-main-page { 308 | /*position:relative*/ 309 | position: absolute; 310 | top: 30px; 311 | height: 95%; 312 | width: 100%; 313 | } 314 | 315 | 316 | /* --- Console control ------------ */ 317 | 318 | #console { 319 | z-index: 30; 320 | pointer-events: none; 321 | /* background-color: rgba(0,0,0,0.8); */ 322 | display: none; 323 | } 324 | 325 | #console-window { 326 | pointer-events: auto; 327 | background-color: rgba(0,0,0,1); 328 | position: absolute; 329 | width: 100%; 330 | text-align: center; 331 | overflow: auto; 332 | max-height: 80%; 333 | } 334 | 335 | #console-buffer { 336 | width: 100%; 337 | position: relative; 338 | color: white; 339 | } 340 | 341 | #console-buffer * { 342 | font: 13px/1 monospace; 343 | } 344 | 345 | #console-buffer > div { 346 | white-space: pre; 347 | text-align: left; 348 | width: 640px; 349 | margin: 0 auto; 350 | } 351 | 352 | .console-error { 353 | color: #f93b00; 354 | } 355 | 356 | .console-message { 357 | color: #eddd98; 358 | } 359 | 360 | .console-example { 361 | color: #b9dffc; 362 | } 363 | 364 | #console-log { 365 | padding-top: 6px; 366 | } 367 | 368 | #console-log > div { 369 | padding-top: 1px; 370 | line-height: 15px; 371 | word-wrap: break-word; 372 | } 373 | 374 | #command-line { 375 | /* border-bottom: 1px solid #ccc; */ 376 | padding-bottom: 10px; 377 | } 378 | 379 | #command-line > * { 380 | display: inline-block; 381 | vertical-align: middle; 382 | } 383 | 384 | #command-line > input { 385 | height: 16px; 386 | color: white; 387 | background-color: transparent; 388 | border: none; 389 | padding: 0; 390 | margin: 0; 391 | outline: none; 392 | width: 500px; 393 | } 394 | 395 | /* --- Layer menu ----------- */ 396 | 397 | #layer-control-btn { 398 | top: 0; 399 | position: absolute; 400 | text-align: center; 401 | z-index: 10; 402 | width: 100%; 403 | } 404 | 405 | body.simplify #layer-control-btn { 406 | display: none; 407 | } 408 | 409 | #layer-control .info-box { 410 | overflow: hidden; 411 | max-height: 90%; 412 | overflow-y: auto; 413 | } 414 | 415 | #layer-menu .layer-list { 416 | margin: 6px 0 6px 0; 417 | } 418 | 419 | #layer-menu .layer-item { 420 | position: relative; 421 | margin-left: -14px; 422 | margin-right: 9px; 423 | padding: 8px 14px; 424 | cursor: pointer; 425 | width: 100%; 426 | } 427 | 428 | .layer-item img { 429 | position: absolute; 430 | top: 8px; 431 | opacity: 0.35; 432 | right: 8px; 433 | width: 16px; 434 | height: 16px; 435 | padding: 2px; 436 | } 437 | 438 | .layer-item img:hover { 439 | opacity: 1; 440 | } 441 | 442 | .layer-item .row { 443 | line-height: 18px; 444 | margin-bottom: 1px; 445 | white-space: nowrap; 446 | } 447 | 448 | .layer-item .layer-name { 449 | display: inline-block; 450 | min-height: 14px; /* For FF when content is empty */ 451 | min-width: 2px; 452 | outline: none; 453 | } 454 | 455 | .layer-item .layer-name.editing { 456 | border-bottom: 1px solid transparent; 457 | } 458 | 459 | .layer-item .layer-name::selection { 460 | background: black; 461 | color: white; 462 | text-decoration: none; 463 | } 464 | 465 | .layer-item.active { 466 | background-color: #e6f7ff; 467 | } 468 | 469 | .layer-item:hover:not(.active) { 470 | background-color: #f7f7f7; 471 | } 472 | 473 | 474 | .layer-item .row > div { 475 | display: inline-block; 476 | } 477 | 478 | .layer-item .col1 { 479 | width: 84px; 480 | } 481 | 482 | .layer-item .col2 { 483 | /*position: relative; 484 | left: 12px; 485 | */ 486 | } 487 | 488 | /* --- Intersection control ----------*/ 489 | 490 | #intersection-display { 491 | cursor: default; 492 | visibility: visible; 493 | display: none; 494 | position: absolute; 495 | z-index: 20; 496 | top: 9px; 497 | right: 14px; 498 | text-align: right; 499 | } 500 | 501 | #intersection-display .text-btn.disabled { 502 | visibility: hidden; 503 | } 504 | 505 | /* --- Popup -------------------- */ 506 | 507 | .popup { 508 | box-sizing: border-box; 509 | position: absolute; 510 | z-index: 40; 511 | top: 12px; 512 | right: 12px; 513 | background-color: white; 514 | border: 1px solid #999; 515 | border-radius: 9px; 516 | padding: 5px 0; 517 | } 518 | 519 | 520 | .popup-content { 521 | box-sizing: border-box; 522 | overflow: hidden; 523 | overflow-y: auto; 524 | padding: 4px 12px; 525 | line-height: 1.2; 526 | font-size: 13px; 527 | } 528 | 529 | .popup td { 530 | vertical-align: top; 531 | /*font-weight: bold;*/ 532 | min-width: 80px; 533 | max-width: 180px; 534 | color: black; 535 | } 536 | 537 | .popup .field-name { 538 | color: #aaa; 539 | padding-right: 8px; 540 | font-weight: normal; 541 | min-width: 60px; 542 | } 543 | 544 | .popup .num-field { 545 | font-style: italic; 546 | } 547 | 548 | .popup .note { 549 | font-style: italic; 550 | text-align: right; 551 | } 552 | 553 | .popup .value { 554 | min-width: 2px; 555 | outline: none; 556 | word-wrap: break-word; 557 | } 558 | 559 | .popup .editable-cell .value.editing { 560 | color: black; 561 | border-bottom: none; 562 | } 563 | 564 | .popup .editable-cell .value::selection { 565 | color: white; 566 | background: black; 567 | } 568 | 569 | 570 | /* --- Map ---------------------- */ 571 | 572 | #map-layers { 573 | height: 100%; 574 | } 575 | 576 | #mshp-main-map { 577 | background-color: #fff; 578 | } 579 | 580 | #map-layers.hover { 581 | cursor: pointer; 582 | } 583 | 584 | #map-layers canvas { 585 | pointer-events: none; 586 | position: absolute; 587 | } 588 | 589 | #map-layers canvas.retina { 590 | transform-origin: top left; 591 | -webkit-transform-origin: top left; 592 | transform: scale3D(0.5, 0.5, 1); 593 | -webkit-transform: scale3D(0.5, 0.5, 1); 594 | } 595 | 596 | #map-layers canvas.highlight-layer { 597 | z-index: 5; 598 | } 599 | 600 | #map-layers canvas.hover-layer { 601 | z-index: 4; 602 | } 603 | 604 | #nav-buttons { 605 | display: none; 606 | z-index: 20; 607 | position: absolute; 608 | top: 6px; 609 | left: 6px; 610 | padding: 2px; 611 | background-color: rgba(255, 255, 255, 0.85); 612 | } 613 | 614 | .nav-btn { 615 | cursor:pointer; 616 | padding: 4px; 617 | line-height: 1; 618 | } 619 | 620 | .nav-btn polygon, 621 | .nav-btn path { 622 | fill: #18799b; 623 | } 624 | 625 | .nav-btn:hover polygon, 626 | .nav-btn:hover path { 627 | fill: black; 628 | } 629 | 630 | .nav-btn.selected path { 631 | fill: white; 632 | } 633 | 634 | .nav-btn.selected { 635 | background-color: #18799b; 636 | border-radius: 4px; 637 | } 638 | 639 | .zoom-box { 640 | position: absolute; 641 | border: 1px solid #f74b80; 642 | z-index: 15; 643 | } 644 | 645 | 646 | /* Simplification control ------------ */ 647 | 648 | @media (max-width: 765px) { 649 | body.simplify #simplify-control-wrapper { 650 | top: 22px; 651 | } 652 | 653 | body.simplify .page-header { 654 | height: 52px; 655 | } 656 | 657 | body.simplify .main-area { 658 | margin-top: 52px; 659 | } 660 | } 661 | 662 | #simplify-control-wrapper { 663 | display: none; 664 | position: absolute; 665 | width: 100%; 666 | overflow: hidden; 667 | margin: 7px auto 0 auto; 668 | top: 0px; 669 | } 670 | 671 | #simplify-control { 672 | text-align: center; 673 | white-space: nowrap; 674 | padding: 0 90px 0 12px; 675 | } 676 | 677 | #simplify-control .slider { 678 | margin: 9px 5px 0 7px; 679 | position: relative; 680 | vertical-align: top; 681 | display: inline-block; 682 | } 683 | 684 | #simplify-control .track { 685 | border-radius: 2px; 686 | width: 340px; 687 | background-color: #fff; 688 | height: 2px; 689 | } 690 | 691 | #simplify-control .handle { 692 | position: absolute; 693 | top: -3px; 694 | } 695 | 696 | #simplify-control .handle img { 697 | width: 20px; 698 | height: 20px; 699 | margin-left: -10px; 700 | margin-top: -10px; 701 | } 702 | 703 | #simplify-control .clicktext { 704 | width: 56px; 705 | background: rgba(255, 255, 255, 0.5); 706 | text-align: center; 707 | border: 1px solid rgba(0,0,0,0); 708 | font-size: 13px; 709 | } 710 | 711 | #simplify-control .clicktext:focus { 712 | background: #fff; 713 | } 714 | -------------------------------------------------------------------------------- /inst/htmlwidgets/mapshaper.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'mapshaper', 4 | 5 | type: 'output', 6 | 7 | initialize: function(el, width, height) { 8 | 9 | // remove position:relative if standalone which 10 | // gives us an parentNode with id = "htmlwidget_container" 11 | if(el.parentNode.id === "htmlwidget_container"){ 12 | el.style.position = "inherit"; 13 | } 14 | 15 | return { 16 | // TODO: add instance fields as required 17 | } 18 | 19 | }, 20 | 21 | renderValue: function(el, x, instance) { 22 | 23 | 24 | // stackoverflow http://stackoverflow.com/questions/27159179/how-to-convert-blob-to-file-in-javascript?rq=1 25 | function blobToFile(theBlob, fileName){ 26 | //A Blob() is almost a File() - it's just missing the two properties below which we will add 27 | theBlob.lastModifiedDate = new Date(); 28 | theBlob.name = fileName; 29 | return theBlob; 30 | } 31 | 32 | // https://developer.mozilla.org/en-US/docs/Web/API/Blob?redirectlocale=en-US&redirectslug=DOM%2FBlob 33 | var jsonFiles = [] 34 | if(Array.isArray(x.files)){ 35 | x.files.forEach( function(fl) { 36 | //if object assume source was a list from R 37 | if(typeof(fl.geojson) === "object") { 38 | fl.geojson = JSON.stringify(fl.geojson); 39 | } 40 | 41 | var bf = blobToFile( 42 | new Blob( 43 | [fl.geojson], 44 | {type : 'application/json'} 45 | ),// the blob 46 | fl.name + ".json" 47 | ) 48 | jsonFiles.push( bf ); 49 | }) 50 | } 51 | 52 | // http://www.2ality.com/2013/06/triggering-events.html 53 | // https://developer.mozilla.org/en-US/docs/Web/Events/drop 54 | var dropEvent = new Event( 55 | "drop", 56 | { 57 | bubbles: true, 58 | cancelable: true 59 | } 60 | ); 61 | 62 | dropEvent.dataTransfer = { files: jsonFiles } 63 | 64 | // do two things here to get the R data integrated 65 | // 1. try to use setTimeout for delayed add once 66 | // mapshaper has finished loading 67 | setTimeout( 68 | function(){document.body.dispatchEvent( dropEvent );} 69 | ,5e2 70 | ) 71 | // 2. if timeout fails, then provide a button for 72 | // manual integration of R data 73 | document.getElementById('r-selection-btn').onclick = function(){ 74 | document.body.dispatchEvent( dropEvent ); 75 | } 76 | }, 77 | 78 | resize: function(el, width, height, instance) { 79 | 80 | } 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /inst/htmlwidgets/mapshaper.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: mapshaper 3 | version: 0.3.6 4 | src: htmlwidgets/lib/mapshaper 5 | script: mapshaper.js 6 | stylesheet: [page.css,elements.css] 7 | -------------------------------------------------------------------------------- /man/mapshaper.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/mapshaper.R 3 | \name{mapshaper} 4 | \alias{mapshaper} 5 | \title{Mapshaper in R} 6 | \usage{ 7 | mapshaper(..., width = "100\%", height = "100\%") 8 | } 9 | \arguments{ 10 | \item{...}{One or many input lists, data.frame, or spatial class. If \code{input} 11 | is not \code{json}, then automatic conversion will be attempted 12 | with \code{\link[geojsonio]{geojson_json}}.} 13 | } 14 | \description{ 15 | Use \href{www.mapshaper.org}{mapshaper} within R to analyze and simplify 16 | your spatial objects. 17 | } 18 | \examples{ 19 | library('geojsonio') 20 | library('sp') 21 | library('mapshaperWidget') 22 | 23 | ### straight from geojson 24 | alabama <- paste0( 25 | readLines( 26 | "https://raw.githubusercontent.com/glynnbird/usstatesgeojson/master/alabama.geojson" 27 | ) 28 | ,collapse = " " 29 | ) 30 | mapshaper(alabama) 31 | 32 | ### example from geojsonio vignette 33 | poly1 <- Polygons( 34 | list( 35 | Polygon(cbind(c(-100,-90,-85,-100), c(40,50,45,40))) 36 | ) 37 | , "1" 38 | ) 39 | poly2 <- Polygons( 40 | list( 41 | Polygon(cbind(c(-90,-80,-75,-90),c(30,40,35,30))) 42 | ) 43 | , "2" 44 | ) 45 | sp_poly <- SpatialPolygons(list(poly1, poly2), 1:2) 46 | 47 | mapshaper( sp_poly ) 48 | 49 | 50 | ### how to provide multiple spatial to mapshaper 51 | calif <- geojson_read( 52 | system.file("examples", "california.geojson", package = "geojsonio") 53 | ) 54 | mapshaper( sp_poly, calif ) 55 | } 56 | 57 | -------------------------------------------------------------------------------- /man/mapshaperOutput.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/mapshaper.R 3 | \name{mapshaperOutput} 4 | \alias{mapshaperOutput} 5 | \title{Widget output function for use in Shiny} 6 | \usage{ 7 | mapshaperOutput(outputId, width = "100\%", height = "400px") 8 | } 9 | \description{ 10 | Widget output function for use in Shiny 11 | } 12 | 13 | -------------------------------------------------------------------------------- /man/renderMapshaper.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/mapshaper.R 3 | \name{renderMapshaper} 4 | \alias{renderMapshaper} 5 | \title{Widget render function for use in Shiny} 6 | \usage{ 7 | renderMapshaper(expr, env = parent.frame(), quoted = FALSE) 8 | } 9 | \description{ 10 | Widget render function for use in Shiny 11 | } 12 | 13 | -------------------------------------------------------------------------------- /mapshaper_htmlwidget.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | --------------------------------------------------------------------------------