{"id":16274049,"url":"https://github.com/trinker/space_manikin","last_synced_at":"2026-01-21T19:35:22.940Z","repository":{"id":15953391,"uuid":"18695929","full_name":"trinker/space_manikin","owner":"trinker","description":null,"archived":false,"fork":false,"pushed_at":"2014-04-12T04:36:46.000Z","size":508,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-14T12:41:35.997Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TeX","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trinker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-04-12T04:07:35.000Z","updated_at":"2018-10-10T06:39:45.000Z","dependencies_parsed_at":"2022-08-30T14:00:25.629Z","dependency_job_id":null,"html_url":"https://github.com/trinker/space_manikin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trinker%2Fspace_manikin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trinker%2Fspace_manikin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trinker%2Fspace_manikin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trinker%2Fspace_manikin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trinker","download_url":"https://codeload.github.com/trinker/space_manikin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247878021,"owners_count":21011158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-10T18:26:52.520Z","updated_at":"2026-01-21T19:35:22.928Z","avatar_url":"https://github.com/trinker.png","language":"TeX","funding_links":[],"categories":[],"sub_categories":[],"readme":"space_manikin\n===\n\n\nIn this post you will learn how to:\n\n1. Create your own quasi-shape file   \n2. Plot your homemade quasi-shape file in `ggplot2`\n3. Add an external svg/ps graphic to a plot   \n4. Change a `grid` grob's color and alpha    \n\n---\n\n## Background \u003cfont size=\"2.5\"\u003e(See [just code](https://raw.githubusercontent.com/trinker/space_manikin/master/space_manikin.R) if you don't care much about the process) \u003c/font\u003e    \n\nI started my journey wanting to replicate a graphic called a *space manikin* by \u003ca href=\"\"\u003eMcNeil (2005)\u003c/a\u003e and fill areas in that graphic like a choropleth.  I won't share the image from McNeil's book as it's his intellectual property but know that the graphic is from a gesturing book that divides the body up into zones (p. 275).  To get a sense of what the manikin looks like here is the `ggplot2` version of it:\n\n\u003cdiv class=\"figure\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/trinker/space_manikin/master/images/manikin1.png\" width=\"300\" height=\"300\"\u003e\n\u003cp id=\"fig1\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 1\u003c/em\u003e: ggplot2 Version of McNeil's (2005) Space Manikin\u003c/p\u003e\n\u003c/div\u003e\n\nWhile this is a map of areas of a body you can see where this could be extended to any number of spatial tasks such as mapping the layout of a room.\n\n---\n\n## 1. Creating a Quasi-Shape File\n\nSo I figured \"zones\" that's about like states on a map.  I have toyed with [choropleth maps](http://trinkerrstuff.wordpress.com/2013/07/05/ggplot2-chloropleth-of-supreme-court-decisions-an-tutorial/) of the US in the past and figured I'd generalize this learning.  The difference is I'd have to make the shape file myself as the \u003ca href=\"maps\"\u003ehttp://cran.r-project.org/web/packages/maps/index.html\u003c/a\u003e package doesn't seem to have McNeil's space manikin.\n\nLet's look at what `ggplot2` needs from the `maps` package:\n\n\n```r\nlibrary(maps); library(ggplot2)\nhead(map_data(\"state\"))\n```\n\n```\n##     long   lat group order  region subregion\n## 1 -87.46 30.39     1     1 alabama      \u003cNA\u003e\n## 2 -87.48 30.37     1     2 alabama      \u003cNA\u003e\n## 3 -87.53 30.37     1     3 alabama      \u003cNA\u003e\n## 4 -87.53 30.33     1     4 alabama      \u003cNA\u003e\n## 5 -87.57 30.33     1     5 alabama      \u003cNA\u003e\n## 6 -87.59 30.33     1     6 alabama      \u003cNA\u003e\n```\n\n\nHmm coordinates, names of regions, and order to connect the coordinates.  I figured I can handle that.  I don't 100% know what a shape file is, mostly that it's a file that makes shapes.  What we're making may or may not technically be a shape file but know we're going to map shapes in `ggplot2` (I use the quasi to avoid the wrath of those who do know precisely what a shape file is).  \n\nI needed to make the zones around an image of a person so I first grabbed a free png silhouette from: http://www.flaticon.com/free-icon/standing-frontal-man-silhouette_10633.  I then knew I'd need to add some lines and figure out the coordinates of the outlines of each cell.  So I read the raster image into R, plotted in `ggplot2` and added lots of grid lines for good measure.  Here's what I wound up with:\n\n\n```r\nlibrary(png); library(grid); library(qdap)\nurl_dl(url=\"http://i.imgur.com/eZ76jcu.png\")\nfile.rename(\"eZ76jcu.png\", \"body.png\")\nimg \u003c- rasterGrob(readPNG(\"body.png\"), 0, 0, 1, 1, just=c(\"left\",\"bottom\"))\nggplot(data.frame(x=c(0, 1), y=c(0, 1)), aes(x=x, y=y)) + \n\tgeom_point() +\n\tannotation_custom(img, 0, 1, 0, 1) + \n\tscale_x_continuous(breaks=seq(0, 1, by=.05))+ \n\tscale_y_continuous(breaks=seq(0, 1, by=.05)) + theme_bw() +\n\ttheme(axis.text.x=element_text(angle = 90, hjust = 0, vjust=0))\n```\n\n![plot of chunk unnamed-chunk-2](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-2.png) \n\n\u003cp id=\"fig2\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 2\u003c/em\u003e: Silhouette from ggplot2 With Grid Lines\u003c/p\u003e\n\n---\n\n### 1b. Dirty Deeds Done Cheap\n\nI needed to get reference lines on the plot so I could begin recording coordinates.  Likely there's a better process but this is how I approached it and it worked.  I exported the ggplot in \u003ca href=\"#fig2\"\u003eFigure 2\u003c/a\u003e into ([GASP](http://www.dramabutton.com/)) Microsoft Word (I may have just lost a few die hard command line folks).  I added lines and figured out the coordinates of the lines.  It looked something like this:\n\n\u003cdiv class=\"figure\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/trinker/space_manikin/master/images/manikin.png\" width=\"500\" height=\"300\"\u003e\n\u003cp id=\"fig3\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 3\u003c/em\u003e: Silhouette from ggplot2 with MS Word Augmented Border Lines\u003c/p\u003e\n\u003c/div\u003e\n\nAfter that I began the tedious task of figuring out the corners of each of the shapes (\"zones\") in the space manikin.  Using \u003ca href=\"#fig3\"\u003eFigure 3\u003c/a\u003e and a list structure in R I mapped each of the corners, the approximate shape centers, and the order to plot the coordinates in for each shape.  This is the code for corners:\n\n\n\n```r\nlibrary(qdap)\ndat \u003c- list(\n    `01`=data.frame(x=c(.4, .4, .6, .6), y=c(.67, .525, .525, .67)),\n    `02`=data.frame(x=c(.35, .4, .6, .65), y=c(.75, .67, .67, .75)),\n    `03`=data.frame(x=c(.6, .65, .65, .6), y=c(.525, .475, .75, .67)),\n    `04`=data.frame(x=c(.4, .35, .65, .6), y=c(.525, .475, .475, .525)),\n    `05`=data.frame(x=c(.35, .35, .4, .4), y=c(.75, .475, .525, .67)),\n    `06`=data.frame(x=c(.4, .4, .6, .6), y=c(.87, .75, .75, .87)),\n    `07`=data.frame(x=c(.6, .6, .65, .65, .73, .73), y=c(.87, .75, .75, .67, .67, .87)),\n    `08`=data.frame(x=c(.65, .65, .73, .73), y=c(.67, .525, .525, .67)),\n    `09`=data.frame(x=c(.6, .6, .73, .73, .65, .65), y=c(.475, .28, .28, .525, .525, .475)),\n    `10`=data.frame(x=c(.4, .4, .6, .6), y=c(.475, .28, .28, .475)),\n    `11`=data.frame(x=c(.27, .27, .4, .4, .35, .35), y=c(.525, .28, .28, .475, .475, .525)),\n    `12`=data.frame(x=c(.27, .27, .35, .35), y=c(.67, .525, .525, .67)),\n    `13`=data.frame(x=c(.27, .27, .35, .35, .4, .4), y=c(.87, .67, .67, .75, .75, .87)),\n    `14`=data.frame(x=c(.35, .35, .65, .65), y=c(1, .87, .87, 1)),\n    `15`=data.frame(x=c(.65, .65, .73, .73, 1, 1), y=c(1, .87, .87, .75, .75, 1)),\n    `16`=data.frame(x=c(.73, .73, 1, 1), y=c(.75, .475, .475, .75)),\n    `17`=data.frame(x=c(.65, .65, 1, 1, .73, .73), y=c(.28, 0, 0, .475, .475, .28)),\n    `18`=data.frame(x=c(.35, .35, .65, .65), y=c(.28, 0, 0, .28)),\n    `19`=data.frame(x=c(0, 0, .35, .35, .27, .27), y=c(.475, 0, 0, .28, .28, .475)),\n    `20`=data.frame(x=c(0, 0, .27, .27), y=c(.75, .475, .475, .75)),\n    `21`=data.frame(x=c(0, 0, .27, .27, .35, .35), y=c(1, .75, .75, .87, .87, 1))\n)\n\ndat \u003c- lapply(dat, function(x) {\n    x$order \u003c- 1:nrow(x)\n    x\n})\n\nspace.manikin.shape \u003c- list_df2df(dat, \"id\")[, c(2, 3, 1, 4)]\n```\n\n\n\nAnd the code for the centers:\n\n\n```r\ncenters \u003c- data.frame(\n    id = unique(space.manikin.shape$id),\n    center.x=c(.5, .5, .625, .5, .375, .5, .66, .69, .66, .5, .34, .31, \n        .34, .5, .79, .815, .79, .5, .16, .135, .16),\n    center.y=c(.597, .71, .5975, .5, .5975, .82, .81, .5975, .39, .3775, .39, \n        .5975, .81, .935, .89, .6025, .19, .14, .19, .6025, .89)\n)\n```\n\n\nThere you have it folks your very own quasi-shape file.  Celebrate the fruits of your labor by plotting that bad oscar.\n\n---\n\n## 2. Plot Your Homemade Quasi-Shape File\n\n\n```r\nggplot(centers) + annotation_custom(img,0,1,0,1) +\n    geom_map(aes(map_id = id), map = space.manikin.shape, colour=\"black\", fill=NA) +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, label = id), color=\"grey60\") \n```\n\n![plot of chunk unnamed-chunk-5](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-5.png) \n\n\u003cp id=\"fig4\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 4\u003c/em\u003e: Plotting the Quasi-Shape File and a Raster Image\u003c/p\u003e\n\nThen I said I may want to tone down the color of the silhouette a bit so I can plot geoms atop without distraction.  Here's that attempt.\n\n\n```r\nimg[[\"raster\"]][img[[\"raster\"]] == \"#0E0F0FFF\"] \u003c- \"#E7E7E7\"\n\nggplot(centers) + annotation_custom(img,0,1,0,1) +\n    geom_map(aes(map_id = id), map = space.manikin.shape, colour=\"black\", fill=NA) +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, label = id), color=\"grey60\") \n```\n\n![plot of chunk unnamed-chunk-6](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-6.png) \n\n\u003cp id=\"fig5\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 5\u003c/em\u003e: Altered Raster Image Color\u003c/p\u003e\n\n---\n\n##  3. Add an External svg/ps    \n\nI realized quickly a raster was messy.  I read up a bit on them in the R Journal ([click here](http://journal.r-project.org/archive/2011-1/RJournal_2011-1_Murrell.pdf)).  In the process of reading and fooling around with [Picas](http://picasa.google.com/) I turned my original silhouette (body.png) blue and couldn't fix him.  I headed back to http://www.flaticon.com/free-icon/standing-frontal-man-silhouette_10633 to download another.  In this act I saw you could download a svg file of the png.  I thought maybe this will be less messier and easier to change colors.  This lead me to a google search and finding the `grImport` package after seeing his [listserve post](https://stat.ethz.ch/pipermail/r-sig-geo/2009-February/005003.html).  And then I saw an article from Paul \u003ca href=\"http://www.jstatsoft.org/v30/i04/\"\u003eMurrell (2009)\u003c/a\u003e and figured I could turn the svg (I didn't realize what svg was until I opened it in Notepad++) into a ps file and read into R and convert to a flexible grid grob.\n\nProbably there are numerous ways to convert an svg to a ps file but I chose a [cloud convert service](https://cloudconvert.org/svg-to-ps).  After I read the file in with `grImport` per the Paul \u003ca href=\"http://www.jstatsoft.org/v30/i04/\"\u003eMurrell (2009)\u003c/a\u003e article.  You're going to have to download the ps file [HERE](https://github.com/trinker/space_manikin/raw/master/images/being.ps) and get to your working directory.\n\n\n\n```r\nbrowseURL(\"https://github.com/trinker/space_manikin/raw/master/images/being.ps\")\n## Move that file from your downloads to your working directory.\n## Sorry I don't know how to automate this.\nlibrary(grImport)\n\n## Convert to xml\nPostScriptTrace(\"being.ps\")\n\n## Read back in and convert to a grob\nbeing_img \u003c- pictureGrob(readPicture(\"being.ps.xml\"))\n\n## Plot it\nggplot(centers) + annotation_custom(being_img,0,1,0,1) +\n    geom_map(aes(map_id = id), map = space.manikin.shape, \n    \tcolour=\"black\", fill=NA) +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, \n    \tlabel = id), color=\"grey60\") \n```\n\n![plot of chunk unnamed-chunk-7](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-7.png) \n\n\u003cp id=\"fig6\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 6\u003c/em\u003e: Quasi-Shape File with Grob Image Rather than Raster\u003c/p\u003e\n\n---\n\n## 4. Change a `grid` Grob's Color and Alpha   \n\nNow we have a flexible grob we can mess around with colors and alpha until our heart's content.\n\n`str` is our friend to figure out where and how to mess with the grob (`str(being_img)`).  That leads me to the following changes to the image to adjust color and/or alpha (transparency).\n\n\n```r\nbeing_img[[\"children\"]][[1]][[c(\"gp\", \"fill\")]] \u003c- \n  being_img[[\"children\"]][[2]][[c(\"gp\", \"fill\")]] \u003c- \"black\"\n\nbeing_img[[\"children\"]][[1]][[c(\"gp\", \"alpha\")]] \u003c- \n  being_img[[\"children\"]][[2]][[c(\"gp\", \"alpha\")]] \u003c- .2\n\n## Plot it\nggplot(centers) + annotation_custom(being_img,0,1,0,1) +\n    geom_map(aes(map_id = id), map = space.manikin.shape, \n    \tcolour=\"black\", fill=NA) +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, \n    \tlabel = id), color=\"grey60\") \n```\n\n![plot of chunk unnamed-chunk-8](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-8.png) \n\n\u003cp id=\"fig6\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 7\u003c/em\u003e: Quasi-Shape File with Grob Image Alpha = .2\u003c/p\u003e\n\n---\n\n## Let's Have Some Fun\n\nLet's make it into a choropleth and a density plot.  We'll make some fake fill values to fill with.\n\n\n```r\nset.seed(10)\ncenters[, \"Frequency\"] \u003c- rnorm(nrow(centers))\n\t\nbeing_img[[\"children\"]][[1]][[c(\"gp\", \"alpha\")]] \u003c- \n  being_img[[\"children\"]][[2]][[c(\"gp\", \"alpha\")]] \u003c- .25\n\nggplot(centers, aes(fill=Frequency)) +\n    geom_map(aes(map_id = id), map = space.manikin.shape, \n    \tcolour=\"black\") +\n\tscale_fill_gradient2(high=\"red\", low=\"blue\") +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, \n    \tlabel = id), color=\"black\") + \n\tannotation_custom(being_img,0,1,0,1) \n```\n\n![plot of chunk unnamed-chunk-9](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-9.png) \n\n\u003cp id=\"fig7\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 8\u003c/em\u003e: Quasi-Shape File as a Choropleth\u003c/p\u003e\n\n\n```r\nset.seed(10)\ncenters[, \"Frequency2\"] \u003c- sample(seq(10, 150, by=20, ), nrow(centers), TRUE)\n\ncenters2 \u003c- centers[rep(1:nrow(centers), centers[, \"Frequency2\"]), ]\n\nggplot(centers2) +\n#\t    geom_map(aes(map_id = id), map = space.manikin.shape, \n#    \tcolour=\"grey65\", fill=\"white\") +\n    stat_density2d(data = centers2, \n        aes(x=center.x, y=center.y, alpha=..level.., \n        fill=..level..), size=2, bins=12, geom=\"polygon\") + \n    scale_fill_gradient(low = \"yellow\", high = \"red\") +\n    scale_alpha(range = c(0.00, 0.5), guide = FALSE) +\n    theme_bw()+ \n    expand_limits(space.manikin.shape) +\n    geom_text(data=centers, aes(center.x, center.y, \n    \tlabel = id), color=\"black\") + \n\tannotation_custom(being_img,0,1,0,1) +\n    geom_density2d(data = centers2, aes(x=center.x, \n        y=center.y), colour=\"black\", bins=8, show_guide=FALSE) \n```\n\n![plot of chunk unnamed-chunk-10](http://dl.dropboxusercontent.com/u/61803503/wp/figure/unnamed-chunk-10.png) \n\n\u003cp id=\"fig8\" style=\"margin-bottom: 3em;\"\u003e\u003cem\u003eFigure 9\u003c/em\u003e: Quasi-Shape File as a Density Plot\u003c/p\u003e\n\t\n\t\nGood times were had by all.\n\n---\n\n\u003cem\u003e\u003cfont size=\"2\"\u003eCreated using the \u003ca href=\"https://github.com/trinker/reports\" target=\"_blank\"\u003ereports\u003c/a\u003e (\u003ca href=\"http://github.com/trinker/reports\"\u003eRinker, 2013\u003c/a\u003e) package\u003c/font\u003e\u003c/em\u003e\n\n\u003cfont size=\"2\"\u003eGet the .Rmd file \u003ca href=\"https://github.com/trinker/space_manikin/blob/master/space_manikin.Rmd\" target=\"_blank\"\u003ehere\u003c/a\u003e\u003c/font\u003e\n\n--- \n## References\n\n- D.  McNeil,   (2005) Gesture \\\u0026 Thought.\n- Paul Murrell,   (2009) Importing Vector Graphics: The {grImport} Package for {R}.  \u003cem\u003eJournal of Statistical Software\u003c/em\u003e  \u003cstrong\u003e30\u003c/strong\u003e  (4)   1-37  \u003ca href=\"http://www.jstatsoft.org/v30/i04/\"\u003ehttp://www.jstatsoft.org/v30/i04/\u003c/a\u003e\n- Tyler Rinker,   (2013) {reports}: {P}ackage to asssist in report writing.  \u003ca href=\"http://github.com/trinker/reports\"\u003ehttp://github.com/trinker/reports\u003c/a\u003e\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrinker%2Fspace_manikin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrinker%2Fspace_manikin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrinker%2Fspace_manikin/lists"}