Coder Social home page Coder Social logo

ncsu-landscape-dynamics / pops-core Goto Github PK

View Code? Open in Web Editor NEW
5.0 8.0 2.0 6.59 MB

PoPS Core: C++ library for the Pest or Pathogen Spread Model

Home Page: https://ncsu-landscape-dynamics.github.io/pops-core

License: GNU General Public License v2.0

C++ 99.31% CMake 0.69%
pathogen spread pops-library pest pests epidemiology model simulation pathogen-spread pops hacktoberfest

pops-core's Introduction

PoPS Core - Pest or Pathogen Spread Model Core

Build Status DOI

PoPS Core is the core C++ library for the PoPS Model.

PoPS (Pest or Pathogen Spread) is a stochastic spread model of pests and pathogens in forest and agricultural landscapes. It is used for various pest, pathogens, and hosts. It was originally developed for Phytophthora ramorum and the original version of the model was written in R, later with Rcpp, and was based on Meentemeyer (2011) paper.

PoPS Core is a header-only C++ library. It is using templates for the main spatial data structures, i.e., rasters, to be universal and it makes use of C++11 features, so C++11 is the minimal required version.

How to cite

If you use this software or code, please cite the following papers:

  • Jones, C., Jones, S., Petrasova, A., Petras, V., Gaydos, D., Skrip, M., Takeuchi, Y., Bigsby, K., and Meentemeyer, R., 2021. Iteratively forecasting biological invasions with PoPS and a little help from our friends. Frontiers in Ecology and the Environment DOI: 10.1002/fee.2357

In case you are using the automatic management feature in rpops or the steering version of r.pops.spread (from the branch steering), please cite also:

  • Petrasova, A., Gaydos, D.A., Petras, V., Jones, C.M., Mitasova, H. and Meentemeyer, R.K., 2020. Geospatial simulation steering for adaptive management. Environmental Modelling & Software 133: 104801. DOI: 10.1016/j.envsoft.2020.104801

In addition to citing the above paper, we also encourage you to reference, link, and/or acknowledge specific version of the software you are using for example:

Contributing

This section is designed to clarify the branch structure and versioning of this repository (and interface repositories) and general naming of new features and bug fix branches, especially those that are take longer to develop.

Branch Structure

  1. master is the stable version of the model that is used for official releases.
  2. fix-issuenumber or fix-bugdescription are branched off of master then merged back via a pull request once bug is fixed.
  3. new_feature is where new features are developed before they are merged into Master via a pull request. For example, infect and vector are currently being developed and will be merged together prior to being merged to master for an official major version release.

Bug Fixes

Most bugs/issues will be found in the master branch as it is the branch being used in the R package and Grass module. Thus bug fixes should be merged into master once tested on both R and Grass. Bug fixes should be released as minor versions (e.g. if major release is 1.0 then the first bug fix would be released as version 1.1 and both R and Grass would be updated to 1.1.0). If a bug is found in one of the interfaces (R package or Grass module) that doesn't require a change to PoPS Core then these repositories should be updated independently and maintain a patch release 1.0.x. For example, if the current version of the R package is 1.1.0 and Grass module is 1.1.0 and a bug is found in the R package then the R package version becomes 1.1.1 while the Grass version is 1.1.0. However, the version number is still shared for all the projects, so when a new version of the GRASS module is needed, it will be 1.1.2.

New Features

When creating new features create a branch from master using the following syntax new_feature. For example, we want to add a transportation network model for human assisted dispersal, the branch created would be named transportation_network_model (or similar). New features will be merged into master once tested based on the priorities of our stakeholders first. Once new features are tested in R and Grass with the latest bug fixes and any other new features being included in the next major release we will merge them into master and create an official major release version (e.g. update from version 1.1 to version 2.0 and the R package and Grass module are updated to 2.0.0). When you are creating branches in your fork, we still recommend choosing informative names such as the one suggested above.

If you are interested in contributing to PoPS and are not a core developer on the model, please take a look at following documents to make the process as seamless as possible.

  1. Contributor Code of Conduct
  2. PoPS Core Style Guide
  3. Contributor Guide

C++ API

The stable API to be used in other projects includes the pops::Model and pops::Config classes and classes used in their API (for example, pops::SpreadRate). This API is changed only between major versions or, if really needed, to fix serious issues in the released major version.

Other classes and functions are part of the internal API and although you can use them in your project, you will need to follow the changes in the library more closely and update your code more often.

If you are using the C++ API, we invite you to open an issue in this repository to tell us about it and we can both acknowledge you in this repo or elsewhere and discuss planned changes with you.

Core Functions

If you are interested in reviewing the code, you may want to focus at the following core functions rather than the API.

simulation.remove : removes the pest or pathogen from the infested hosts based on some environmental threshold (currently only temperature is accounted for).

simulation.generate : generates dispersing individuals from all infested cells based as a function of local infected hosts and weather.

simulation.disperse : creates dispersal locations for the dispersing individuals from the generate function.

simulation.mortality : causes mortality in infested/infected hosts based on mortality rate

The custom date class is used to easily manage different time steps within the model and account for differences in the way frequently used weather data sets treat leap years (DAYMET drops December 31st from leap years, PRISM keeps all days even for leap years)

Using the model

The PoPS Core library can be used directly in a C++ program or through other programs. It is used in R package called rpops and a GRASS GIS module called r.pops.spread.

Integrating the library into your own project

As a Git submodule

This is a convenient way, if you are using Git and you can use the C++ header files directly.

Git supports inclusion of other repositories into your own code using a mechanism called submodules. In your repository, run:

git submodule add https://github.com/ncsu-landscape-dynamics/pops-core

If you want a specific branch of PoPS Core, after adding the PoPS submodule, run the following commands (with branch-name being the branch of the PoPS library you want to use):

cd pops-core
git checkout origin/branch-name

The will create a directory called pops-core in your repository which will now contain all the files from this repository. You can use the two following commands to see the changes to your repository:

git status
git diff --cached

Git added a file called .gitmodules with the link to this repository and linked a specific commit in this repository. The commit linked is the currently latest commit to PoPS library.

You can now commit and push changes to your repository.

When someone else clones our project, they need to run the two following commands to get the content of the pops-core directory:

git submodule init
git submodule update

Alternatively, the pops-core directory can be populated during cloning when git clone is used with the --recurse-submodules parameter.

If you want to update the specific PoPS commit your repository is using to the latest one, you the following command:

git submodule update --remote

Compile and test

Here we are assuming that you use Linux command line or equivalent and you have CMake and C++ compiler installed. We are testing with GNU GCC with (g++) and GNU make (make), but many of other tools supported by CMake should work too. See CMake documentation for different ways of compiling.

First download the source code (as a ZIP file and unpack it or use Git to get it from the Git repository).

Configure the project and use directory called build for configure and build outputs:

cmake -S . -B build

Build the project:

cmake --build build

The library itself does not need compilation since it is header only (it is compiled later with your project), but this compiled several test programs.

To run these tests:

cmake --build build --target test

If something is wrong, this will generate error messages. Note that not all tests are not fully automatic, so in couple cases this only testing if the code is running and not crashing (you will need to examine the source code to see the details).

Additionally, create documentation using the following (Doxygen required):

cmake --build build --target docs

The HTML documentation will appear in the html subdirectory of build directory. Open the file called index.html to access it in a web browser.

Optionally, to remove the build directory when you are done, use:

rm -rf build

Compiling as part of another project

Note that if you are not using CMake, you can just add the headers to your project since this a header-only library. However, if you are using CMake, you probably want to use the following approach.

Assuming you added the directory as a submodule or a plain subdirectory called pops-core add these two following lines to your CMakeLists.txt file (assuming you already have target called your_target):

add_subdirectory(pops-core)
target_link_libraries(your_target PRIVATE pops-core)

Authors and contributors

Authors

(ordered by number of commits)

  • Vaclav Petras
  • Chris Jones
  • Anna Petrasova

Previous contributors

(ordered alphabetically)

  • Zexi Chen
  • Devon Gaydos
  • Margaret Lawrimore
  • Francesco Tonini

See Git commit history, GitHub insights, or CHANGELOG.md file (if present) for details about contributions.

License

Permission to use, copy, modify, and distribute this software and its documentation under the terms of the GNU General Public License is hereby granted. No representations are made about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. See the GNU General Public License for more details.

pops-core's People

Contributors

chrisjones687 avatar jay1204 avatar malawrim avatar petrasovaa avatar wenzeslaus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pops-core's Issues

Release version 1.0.0

Steps:

  1. Get this repo ready to be tagged for 1.0.0
  2. Make the other repos up to date
  3. Tag this repo v1.0.0
  4. Use v1.0.0 in the other repos
  5. Tag other repos v1.0.0
  6. Announce release of 1.0.0

Todo list before tagging pops-core v1.0.0:

  • Complete issues in this repo with milestone 1.0.0
  • Rename this repo to pops-core
  • Update name in rpops
  • Update name in r.pops.spread
  • Update rpops
  • Update r.pops.spread
  • Specify release policy for core, R, GRASS ("minor mostly for interfaces") - #107
  • Update and sync references and authors in all 3 repos - #109
  • Remove Makefile
  • Update contributing ("branches") - #107

Todo list before v1.0.0 announcement:

Reduce number of warnings reported during compilation

There is a long list of warnings reported during the compilation. Reduce the list to make the warnings more useful and harder to ignore or miss. Possibly reduce the number to zero even if ignoring a particular warning is required.

Add in pesticide treatment option

Pesticide treatment would be yet another treatment option that would move a percent of the infected and susceptible hosts in a cell, based on the efficacy (parameter) of the treatment to a resistant pool for a specified (maybe with a probability curve) duration (parameter) (e.g. if a fungicide is known to work on ~95% of the population for ~45 days then the function would move 95% of the susceptible and infected hosts in those treated cells to the resistant pool for 45 days). We need to track these resistant moves similar to the way we do for mortality.

Started something here in a rpops branch. We just need to think through the best way to implement this in an easy to use and flexible manner.

Exposed classed not being managed with Treatment

Currently, the exposed class is not being removed during treatment. This should be an easy fix using treatment.manage_mortality and I have a version of this running locally in rpops and will push here once I confirm it works. We may want to rename treatments.manage_mortality but don't have a good solution.

Add how to contribute

We already have a CONTRIBUTING file, but we need to add content for first time visitors (not only code, but also issues etc.).

Implement additional kernels

Based on comment by @ChrisJones687 in #19:

Kernels to implement

(comes from data on seed dispersal but very applicable)
...

Available in C++11

  • Exponential: single parameter should be 1/(mean distance (this is calculated in MCMC))
  • Gamma: double parameter (shape (1.0) and scale(estimated))
  • Cauchy: double parameter (shift (0.0) and scale(estimated))
  • Weibull: double parameter (shape (1.0) and scale (estimated))
  • Normal: double Parameter (mean (0.0) and sd (estimated))
  • Lognormal: double parameter (mean (0.0) and sd (estimated))

Available in Boost, but not available in standard C++

Available in boost C++ math library

  • WALD or Inverse Gaussian
  • Logistic distribution

Others to be implemented, not in C++11 or Boost

  • 2Dt
  • Power Law (Cauchy is one variant of a power law)
  • log hyperbolic secant
  • Exponential Power

#19 is now resolved by #27, so this can be implemented.

total_hosts in Model is actually not total hosts, it is "all different individuals"

I realized there was a reason I never renamed total_plants to total_hosts. Even in simple SI model without pest resistance, S + I != total_plants because the model includes a dilution effect where number of all (not only host) plants in the cell influences the establishment:

double probability_of_establishment =
                                (double)(susceptible(row, col)) / total_hosts(row, col);

https://github.com/ncsu-landscape-dynamics/PoPS/blob/master/include/pops/simulation.hpp#L419

This is in the disperse function.

On the other hand, the newer movement function assumes S + I == total_hosts. I don't think the limitation/assumption in movement necessitates that the whole model needs to require the same condition as long as it is documented that the movement can only be used when the condition is met.

Vector

Add transmission between two simulations one for a vector and one for a host. Also, add the ability to track 2 separate pests or pathogens.

Simulation doesn't run in certain cases

I ran into this really weird problem that nothing spreads and it is related to the size (or rather aspect ratio) of the region. The spores seems to be generated in function generate but then in variable dispersers there are only zeros. In test_simulation.cpp you can use these lines

Raster<int> infected = {{0, 0, 0, 5, 5}, {0, 0, 0, 5, 5}, {0, 0, 0, 0, 0}};
Raster<int> mortality_tracker = {{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};
Raster<int> susceptible = {{10, 6, 8, 8, 8}, {14, 15, 12, 12, 12}, {14, 15, 12, 12, 12}};
Raster<int> total_plants = {{15, 6, 10, 10, 10}, {14, 15, 14, 14, 10}, {14, 15, 14, 14, 10}};
Raster<double> temperature = {{5, 0, 5, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};
Raster<double> weather_coefficient = {{0.6, 0.8, 0.7, 0.7, 0.7}, {0.2, 0.8, 0.8, 0.8, 0.8},{0.6, 0.8, 0.7, 0.7, 0.7}};

and basically when infection is X below, it doesn't run.

O O X X
O O O O

O O O X
O O O X
O O O O

O O O X X
O O O X X
O O O O O

this is my modification of function 'generate' to debug it:

void generate(const IntegerRaster& infected,
              bool weather, const FloatRaster& weather_coefficient,
              double reproductive_rate)
{
    std::cout << "generate" << std::endl;
    double lambda = reproductive_rate;
    for (unsigned i = 0; i < height; i++) {
        for (unsigned j = 0; j < width; j++) {
            if (infected(i, j) > 0) {
                if (weather)
                    lambda = reproductive_rate * weather_coefficient(i, j); // calculate
                int dispersers_from_cell = 0;
                std::poisson_distribution<int> distribution(lambda);
               
                for (int k = 0; k < infected(i, j); k++) {
                    dispersers_from_cell += distribution(generator);
                }
                dispersers(i, j) = dispersers_from_cell;
                std::cout << "save " << dispersers(i, j) << " " << i << " " << j << std::endl;
            }
            else {
                dispersers(i, j) = 0;
            }
        }
    }
    for (unsigned i = 0; i < height; i++) {
        for (unsigned j = 0; j < width; j++) {
            if (dispersers(i, j) > 0) {std::cout  << "read "<< i << " " << j << std::endl;}
        }
    }
    std::cout << "generate end" << std::endl;
}

fails build on macOS

Discovered during R cmd check on macOS. Need to add override to get_start and get_end functions in treatment

Add citation information to the repo

What needs to be done

A simple citation can be in the readme file. The question is if it is a paper or the software itself or both.

Current state

The readmes mention Meentemeyer (2011) and Tonini (2017) papers.

r.pops.spread is has the references and authors sections in the documentation as all GRASS GIS modules do:

https://grass.osgeo.org/grass78/manuals/addons/r.pops.spread.html#references

and additionally, the repo experimentally includes also Citation File Format (CFF) file:

https://github.com/ncsu-landscape-dynamics/r.pops.spread/blob/master/CITATION.cff

I used CFF after an investigation into software citations. It is a good bet as it is relatively simple to read and write for humans but it can handle more complex cases. The issue is that for both R and GRASS GIS, it duplicates what you need/want to have elsewhere.

rpops has authors in the R's DESCRIPTION file:

https://github.com/ncsu-landscape-dynamics/rpops/blob/master/DESCRIPTION

What about interfaces?

I think the interfaces should link to the core repo. How? They already do in the readme, but in terms of citation they can include PoPS Core in the references if we decide to promote citation for the software/repo itself. If not, then same paper used everywhere, nice and simple.

The recommend citation can be just the same one, i.e., PoPS Core, not the interface. This is simple and almost the same as if we use paper everywhere.

CFF has references section which can include both a paper and dependencies. The "message" can explain the relation and the need/recommendation to reference both or all (interface, core, paper).

How does this relate to DOI?

If we get DOI for PoPS Core, then we need to include it in the interfaces. We can also get one DOI for all... A DOI for each repo is probably best supported: GitHub: Making Your Code Citable. It is not 100% clear to me, but perhaps the Concept and Version DOI can be resolved in this way too. (It seems that registering the repo before the release is needed.)

I think the downside of DOIs here is that adding DOI to repo changes the content, so basically invalidating the DOI as a reference to an exact version.

Revisit and/or fix gh-pages failing with invalid key format

The deploy step of the gh-pages job fails with

  /usr/bin/ssh-add /home/runner/.ssh/github
  Error loading key "/home/runner/.ssh/github": invalid format
  ##[error]Action failed with "The process '/usr/bin/ssh-add' failed with exit code 1"

which does not seem to be on our end, but it appeared after migration of gh-pages from Travis to GH Actions and rebuild does not help.

Fortunately, this does not influence the older version of the documentation, so that's still accessible and can be linked elsewhere if needed.

Add season class

Add season class for seasonality and make season class work even if selected season start month is later than end month (e.g. November to May)

Move the utility functions and defs to a separate file

There is couple of general utility functions or other definitions in the code. Some wrapping or extending standard library facilities. Move these into as separate file.

Some defs many need to be in a separate file such as an index type which might be needed to at least partially resolve -Wsign-compare from #72.

Update Spread rate timing

Add the capability for spread rates to be calculated at every output interval rather than just yearly. Should be able to be handled in config similarly to quarantine stats. We should default to handling all of our statistical outputs this way and add a use_spreadrates (or whatever stat) bool to turn off the functionality if we don't need to use it for that case study.

Create movements class

The current implementation of movements is not ideal. We would like to create a class that is more similar to treatments to allow for more flexibility.

Add in support of multi-host

implement support for multi-host for up to n hosts. This should account for host susceptibility and transmissibility.

Spread rate computation

To compute spread rate, I suggest to implement function which computes the most northern, southern, eastern and western infection to get an infection bbox. This would be applied to the initial infection raster and the last infection rasters. The average of values of all the stochastic runs would be compared to the values from initial infection to compute spread rate.
GRASS r.pops.spread would probably export a vector with 2 bboxes (initial and averaged max spread from runs), not sure what's the best for rpops.

Add mortality

Add mortality to the library instead of handling it in each interface separately.

Add Transportation Network Model

Based on the spotted lanternfly case study it is clear the need for some type of spread via transportation networks. For example, spotted lanternfly spreads long distances along railways by hitchhiking on trains and hopping off along the route. This could be done via a cost matrix (railway corridors speed up spread, forest areas slow spread, etc.

Generic code for dispersal kernels

Wrap all dispersal kernel code into a generic API (function or class) so that no complexity of dispersal kernel code is in the Simulation itself, but instead, it is handled separately and a customizable way. This should remove all (or most) of the parameters related to kernels from the Simulation.disperse() function resulting in simpler Simulation calls and possible higher customization of kernels.

However, the possibility to modify kernel parameters during the simulation will be preserved because the whole kernel function/object can be replaced or modified when calling the Simulation.disperse() function.

Add management

Add capability to pass management polygons to modify host map for susceptible and infected.

Add quarantine escape functionality

Add functionality to compute the following metrics:

  • Probability of escaping quarantine
  • Year of escape
  • Distance to quarantine

Possible implementation (in statistics.hpp):

  • New class for quarantine
    • raster input representing quarantine area(s) (0 - no quarantine, 1, 2, 3 ... separate quarantine areas)
    • finds N, S, E, W boundary of the area(s)
    • method that gets infected cell raster and for each infected cells finds out out if inside area, and if yes, computes distance in 4 directions to the boundary. Reports if inside or not and minimum distances in all directions (or just the direction with the minimum distance).
    • aggregate across all quarantine areas (alternatively report separately)
  • Function to aggregate the results of this method for multiple runs to compute the probability of escape, report the distribution of min distances.

Missing instructions where to fix bugs

We miss instructions for core developers on where to put fixes. master? development (now 11 commits behind master)? fix/... branch (from what)? pull request (against what)?

Example, I have the fix for #13, but it is not clear where to put it.

Relates to #2 (originally meant for external contributors).

Add Resistant pool(s)

Add in the resistant pool(s). The idea here is that it is another compartment in the model but one that is ignored for most calculations of spread but is used more for treatment using pesticides (related to #33 ) or if resistant is obtained when an individual recovers from a disease (unaware of this in plant systems but happens often in human and animal diseases).

This will include a function to return hosts from the resistant pool to the susceptible pool after a defined length of time. The question is whether option 1) one large pool that could be imported and exported with a bunch of small trackers with separate return times option or 2) have a bunch of separate pools that are linked to their return times that can be imported/exported makes the most sense and is the most flexible.

Started something here in a rpops branch. We just need to think through the best way to implement this in an easy to use and flexible manner.

Rename this repo and project to pops-core

Rename this repo and project from PoPS to pops-core. The C++ headers should be still in pops directory and the C++ namespace still pops. The -core means something along the lines of lib or main part, so no reason to add it to header location or namespace. It is core in the relation to rest of the PoPS project which should be the one using the name PoPS.

Bug in current treatment scheduling

There seems to be problem in current treatment scheduling, specifically it shows up when run weekly and treatment is scheduled at the end of the year, e.g. scheduled in the first year 12-31. The condition as it is now:
https://github.com/ncsu-landscape-dynamics/PoPS/blob/master/treatments.hpp#L118

results in completely skipping the 12-31 treatment. It's caused somehow by the syncing to 1/1 when run weekly. I am not sure how to properly fix it, maybe line
https://github.com/ncsu-landscape-dynamics/PoPS/blob/master/treatments.hpp#L117
needs to add actual 7 days and not use the special increased_by_week function, but I would have to make more tests if that actual works for all cases.

The new scheduling mechanism shouldn't have this problem, so if we adopt it, I don't think it's worth spending time fixing this.

Rename ShortLongDispersalKernel to natural/anthro or to sth more generic

The ShortLongDispersalKernel class template follows the original naming convention of short and long distance dispersal. Now we call it natural and anthropogenic. The kernel class itself is more general, so there is more options and it is just a question of naming.

ShortLongDispersalKernel(short_kernel, long_kernel, use_long_kernel, percent_short_dispersal)
NaturalAnthropogenicDispersalKernel(natural_kernel, anthropogenic_kernel, use_anthropogenic_kernel, percent_natural_dispersal)
PrimarySecondaryDispersalKernel(primary_kernel, secondary_kernel, use_secondary_kernel, percent_primary_dispersal)
DoubleDispersalKernel(first_kernel, second_kernel, use_second_kernel, percent_first_dispersal)

A separate, yet related, issue is parametrize usage of the kernels. Now it is either first one or both, alternative is supporting not using the first one. We could also have the types (the enum) as a parameter instead of boolean (now called use_long_kernel).

This is mostly about API beauty and it should be easy to keep backwards compatibility, so there is no pressure.

Build with Clang too in CI

Build also with Clang, not only GCC, in the CI.

Motivation: #111, #112, -Wall result in different warnings being enabled, rpops is build with clang on macOS.

Edit date class for DAYMET data

DAYMET drops December 31st from leap years therefore when simulating leap years using DAYMET as our data source we should use that convention as well otherwise our weather data will not match up with our simulation time. Only really an issue when running over long time periods.

Move to SEI framework

This update will move susceptible individuals to exposed for a latent period (x number of days) and after that latent period the exposed group will become infected.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.