diff --git a/.travis.yml b/.travis.yml index 85a92d7884..e6b4d9a49d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,30 @@ language: node_js -sudo: false +# At this time, the travis trusty sudo environment works, but the sudo: false +# environment doesn't (it might with a bunch of apt packages). +sudo: required +dist: trusty + node_js: - '0.12' +addons: + firefox: latest + apt: + packages: + # I suspect that not all of these are necessary + - mesa-utils + - xvfb + - libosmesa6 + + - libgif-dev + - libpng-dev + + - freeglut3-dev + - libxmu-dev + - libxi-dev + - libxxf86vm-dev + - libxrandr-dev + cache: directories: - node_modules @@ -12,6 +34,10 @@ before_install: - CACHE="${HOME}/cache" CMAKE_VERSION=3.5.0 CMAKE_SHORT_VERSION=3.5 source ./scripts/install_cmake.sh - npm prune +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + script: - npm run build - npm run docs diff --git a/CMakeLists.txt b/CMakeLists.txt index b84fe1f992..818d6ab0ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ enable_testing() set(BUILD_TESTING ON CACHE BOOL "Enable geojs testing") set(PHANTOMJS_TESTS ON CACHE BOOL "Generate phantomjs unit tests.") +set(FFHEADLESS_TESTS ON CACHE BOOL "Generate headless Firefox unit tests (requires xvfb to be running).") +set(TEST_SAVE_IMAGE "none" CACHE STRING "Save headless test images even if there aren't errors. Valid options are none, all, or the a comma-separated list of test names.") set(ESLINT_TESTS ON CACHE BOOL "Generate eslint style tests for JS source files.") set(SELENIUM_TESTS ON CACHE BOOL "Generate selenium unit tests.") @@ -70,6 +72,13 @@ add_test( ) set_property(TEST "notes-report" APPEND PROPERTY DEPENDS "notes-reset") +add_test( + NAME "total-coverage" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND npm run combine-coverage +) +set_property(TEST "notes-report" APPEND PROPERTY DEPENDS "notes-reset") + if(PHANTOMJS_TESTS) find_program(NPM_EXECUTABLE npm) add_test( @@ -80,8 +89,52 @@ if(PHANTOMJS_TESTS) set_property(TEST "phantomjs" APPEND PROPERTY ENVIRONMENT "CTEST_NOTES_PATH=${CMAKE_CURRENT_BINARY_DIR}/notes") set_property(TEST "phantomjs" APPEND PROPERTY DEPENDS "notes-reset") set_property(TEST "notes-report" APPEND PROPERTY DEPENDS "phantomjs") + set_property(TEST "total-coverage" APPEND PROPERTY DEPENDS "phantomjs") +endif() + +add_custom_target( + data_files + ALL + DEPENDS ${Girder_DOWNLOAD_FILES} +) +add_test(NAME get_data_files COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target data_files) + +if(FFHEADLESS_TESTS) + find_program(NPM_EXECUTABLE npm) + add_test( + NAME "ffheadless" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND npm run ffci + ) + set_property(TEST "ffheadless" APPEND PROPERTY ENVIRONMENT "CTEST_IMAGE_PATH=${CMAKE_CURRENT_BINARY_DIR}/images") + set_property(TEST "ffheadless" APPEND PROPERTY ENVIRONMENT "TEST_SAVE_IMAGE=${TEST_SAVE_IMAGE}") + set_property(TEST "total-coverage" APPEND PROPERTY DEPENDS "ffheadless") + set_property(TEST "ffheadless" APPEND PROPERTY DEPENDS "get_data_files") + + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/testing/test-runners/baseline_images.py" + "${CMAKE_CURRENT_BINARY_DIR}/test/baseline_images.py" + COPYONLY + ) endif() +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/base-images.tgz" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + # Make sure we have the data files. + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target data_files + # Run the ffheadless test, asking to save all images + COMMAND TEST_SAVE_IMAGE=all npm run ffci + # Make a tarball of all of the images + COMMAND tar -zcvf "${CMAKE_CURRENT_BINARY_DIR}/base-images.tgz" --exclude=*-test.png --exclude=*-diff.png --exclude=*-base.png -C "${CMAKE_CURRENT_BINARY_DIR}/images" . + COMMENT "Create baseline images, then tar them into a single file" + VERBATIM +) + +add_custom_target(baseline_images DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/base-images.tgz") + +add_test(NAME baseline_images CONFIGURATIONS "baseline_images" COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target baseline_images) +set_property(TEST "baseline_images" APPEND PROPERTY ENVIRONMENT "CTEST_IMAGE_PATH=${CMAKE_CURRENT_BINARY_DIR}/images") + if(SELENIUM_TESTS) find_package(PythonInterp REQUIRED) diff --git a/cmake/travis_build.cmake b/cmake/travis_build.cmake index 8c6f9be3c4..bbf08f653d 100644 --- a/cmake/travis_build.cmake +++ b/cmake/travis_build.cmake @@ -5,7 +5,7 @@ include(${CTEST_SOURCE_DIRECTORY}/CTestConfig.cmake) set(CTEST_SITE "Travis") set(CTEST_BUILD_NAME "Linux-$ENV{TRAVIS_BRANCH}") set(CTEST_CMAKE_GENERATOR "Unix Makefiles") -set(coverage_file "${CTEST_SOURCE_DIRECTORY}/dist/cobertura/phantomjs/coverage.xml") +set(coverage_file "${CTEST_SOURCE_DIRECTORY}/dist/cobertura/cobertura-coverage.xml") ctest_start("Continuous") ctest_configure( @@ -15,6 +15,7 @@ ctest_build() ctest_test(PARALLEL_LEVEL 1 RETURN_VALUE res) if(EXISTS "${coverage_file}") file(COPY "${coverage_file}" DESTINATION "${CTEST_BINARY_DIRECTORY}") + file(RENAME ${CTEST_BINARY_DIRECTORY}/cobertura-coverage.xml ${CTEST_BINARY_DIRECTORY}/coverage.xml) ctest_coverage() file(REMOVE ${CTEST_BINARY_DIRECTORY}/coverage.xml) endif() diff --git a/docs/baseline_images.rst b/docs/baseline_images.rst new file mode 100644 index 0000000000..2d0316b79b --- /dev/null +++ b/docs/baseline_images.rst @@ -0,0 +1,5 @@ +baseline_images +=============== + +.. automodule:: baseline_images + :members: diff --git a/docs/developers.rst b/docs/developers.rst index 327746601b..552f7b1835 100644 --- a/docs/developers.rst +++ b/docs/developers.rst @@ -7,32 +7,8 @@ Developer's guide This guide assumes you have cloned and built the geojs repository according to the :ref:`Quick start guide `. -The selenium testing infrastructure of Geojs is run via CTest, it assumes -that the testing "server" is started prior to execution. To start the -server, just run :: - - npm run start-test - -This will start a server on the default port of ``30100``. The port -and selenium host names are configurable with cmake. For example inside -the Kitware firewall, you can run the following to test on the selenium -node on ``garant`` :: - - cmake -DSELENIUM_TESTS=ON -DSELENIUM_HOST=garant /path/to/geojs - make - ctest -VV - -You may need to also set the variable ``TESTING_HOST`` to your computer's -IP address reachable by the selenium node. - -.. note:: - - Typically, CMake is used to build outside of the source tree. This - means you would create a new directory somewhare and point cmake - to the geojs source directory. You may need to rerun ``cmake`` and - ``make`` after making changes to your code for everything to - build correctly. Try running ``ccmake /path/to/geojs`` for a full - list of configuration options. + To run all of the tests, you will need the optional packages and python + modules listed there. Geojs employs several different frameworks for unit testing. These frameworks have been designed to make it easy for developers to @@ -49,6 +25,15 @@ tests are preformed automatically for every file added to the build; no additional configuration is required. You can run a quick check of the code style outside of CMake by running ``npm run lint``. +Code coverage +------------- + +Code coverage information is generated automatically for all headless unit tests +by Karma's test runner when running ``npm run test``. The coverage information is +submitted to `codecov `_ and +`cdash `_ after every +successful Travis run. + Headless browser testing ------------------------ @@ -78,9 +63,73 @@ instrumentation after running. Ideally, each test should be runnable independently and use jasmines ``beforeEach`` and ``afterEach`` methods for setup and tear down. +Headless WebGL testing +---------------------- + +To fully test code that uses WebGL, a browser with WebGL is required. +If xvfb, osmesa, and Firefox are installed, some tests can be run in a virtual +frame buffer that doesn't require a display. May of these tests depend on +additional data which can be downloaded by using CMake and running ctest. + +For example, running :: + + cmake /path/to/geojs + make + xvfb-run -s '-ac -screen 0 1280x1024x24' ctest -VV -R ffheadless + +will run the headless WebGL tests. After the data for tests is downloaded, +the tests can also be run via ``npm run test-webgl``, which assumes that +``xvfb-run`` is available. + +The headless unit tests that require WebGL should be placed in the +``tests/gl-cases/`` directory. When tests are run in a normal browser via +``npm run start``, the webgl tests are included. + +Many of these tests compare against a baseline image. If a test is changed or +added, new baselines can be generated and optionally uploaded via the script +built into ``test/baseline_images.py``. + +If a test fails, the specific test will be reported by the test runner, and the +base and test images are saved in the ``images`` subdirectory of the build +directory. The images have the base name of the test and end in ``-base.png`` +for the reference image, ``-test.png`` for the current test, and ``-diff.png`` +for a difference image where areas that are different are highlight (using +resemblejs, the default highlight color is pink). + +Unless an image comparison test fails, images are not automatically saved. To +save all images, add the environment variable ``TEST_SAVE_IMAGE=all`` to the +test command or set this parameter in CMake. + +.. note:: + + Typically, CMake is used to build outside of the source tree. This + means you would create a new directory somewhere and point cmake + to the geojs source directory. You may need to rerun ``cmake`` and + ``make`` after making changes to your code for everything to + build correctly. Try running ``ccmake /path/to/geojs`` for a full + list of configuration options. + Selenium testing ---------------- +The selenium testing infrastructure of Geojs is run via CTest, it assumes +that the testing "server" is started prior to execution. To start the +server, just run :: + + npm run start-test + +This will start a server on the default port of ``30100``. The port +and selenium host names are configurable with cmake. For example inside +the Kitware firewall, you can run the following to test on the selenium +node on ``garant`` :: + + cmake -DSELENIUM_TESTS=ON -DSELENIUM_HOST=garant /path/to/geojs + make + ctest -VV + +You may need to also set the variable ``TESTING_HOST`` to your computer's +IP address reachable by the selenium node. + Most tests for geojs require a full browser with webgl support. For these test, a framework based on `Selenium `_ is provided. This test framework is intentionally lightweight to allow @@ -206,11 +255,3 @@ before running this script. Note: you must have write permission in the MIDAS GeoJS community before you can upload new images. Contact a community administrator for an invitation. -Code coverage -------------- - -Code coverage information is generated automatically for all headless unit tests -by Karma's test runner when running ``npm run test``. The coverage information is -submitted to `codecov `_ and -`cdash `_ after every -successful Travis run. diff --git a/docs/index.rst b/docs/index.rst index 9b796b71da..be79875c1e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,7 @@ If you have any questions or comments, feel free to join us on our quickstart users developers + provisioning testingutils Indices and tables diff --git a/docs/provisioning.rst b/docs/provisioning.rst new file mode 100644 index 0000000000..6f7307bd01 --- /dev/null +++ b/docs/provisioning.rst @@ -0,0 +1,76 @@ +============================ +Provisioning for Development +============================ + +.. _ubuntu-development: + +Ubuntu 14.04 +------------- + +This shows how to set up a build and test environment in Ubuntu 14.04, using +all but the Selenium-based tests. + +These instructions will probably work for any Ubuntu release from 14.04 +onward. They assume a basic installation, as, for instance, from the +`HashiCorp ubuntu/trusty64 image `_. + +Add nodejs to the sources so it can be installed :: + + wget -qO- https://deb.nodesource.com/setup_4.x | sudo bash - + +Install required packages (you may want to also include cmake-curses-gui for +convenience in configuring CMake options) :: + + sudo apt-get install --yes \ + cmake \ + firefox \ + git \ + libjpeg8-dev \ + libpango1.0-dev \ + mesa-utils \ + nodejs \ + python-pip \ + xvfb + +Install node-gyp, which is required to build the node canvas module :: + + sudo npm install -g node-gyp-install && /usr/lib/node_modules/node-gyp-install/bin.js + +Checkout the GeoJS source and change to the source directory :: + + git clone https://github.com/OpenGeoscience/geojs.git + cd geojs + +Install node modules :: + + npm install + +Build GeoJS and run some basic tests :: + + npm run build + npm run lint + npm run test + +Note that some of the tests measure speed, and therefore may fail if you are +running on slow hardware or in a limited virtual machine. + +Use CMake to create additional tests and make to download test data :: + + cmake . + make + +Run the headless WebGL tests :: + + xvfb-run -s '-ac -screen 0 1280x1024x24' ctest -VV -R ffheadless + +Run all but the Selenium tests :: + + xvfb-run -s '-ac -screen 0 1280x1024x24' ctest --output-on-failure -E selenium + +Install python packages :: + + pip install --user girder-client + +Generate new baseline images for the headless WebGL tests :: + + python test/baseline_images.py --xvfb --generate --upload --verbose diff --git a/docs/quickstart.rst b/docs/quickstart.rst index f06985b177..5bfef95e4d 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -12,16 +12,29 @@ The following software is required to build geojs from source: * `Git `_ * `Node.js `_ -In addition, the following python modules are recommended for development -and testing of geojs. +For testing and development, the following additional software is required: * `Python 2.7 `_ -* `Make `_ * `CMake `_ -* `Pillow `_ + +In addition, the following python modules are recommended for development +and testing of geojs. + +* `Girder Client `_ +* `Pillow `_ * `Requests `_ * `Selenium `_ +For testing WebGL in a headless environment, the additional packages are needed: + +* `mesa-utils `_ and `libosmesa6 `_ +* `xvfb `_ +* `Firefox `_ + +For an example on how to install all packages for a specific OS, see +:ref:`Ubuntu 14.04 Provisioning `. + + Getting the source code ----------------------- diff --git a/docs/testingutils.rst b/docs/testingutils.rst index 7d88b0690e..c02cfdd26e 100644 --- a/docs/testingutils.rst +++ b/docs/testingutils.rst @@ -6,6 +6,7 @@ Testing infrastructure .. toctree:: :maxdepth: 2 + baseline_images selenium_test midas_handler upload_test_cases diff --git a/karma-base.js b/karma-base.js index 54b99b8a71..828387e597 100644 --- a/karma-base.js +++ b/karma-base.js @@ -3,6 +3,7 @@ var url = require('url'); var fs = require('fs'); var path = require('path'); var notes_path = process.env.CTEST_NOTES_PATH || path.resolve('notes'); +var image_path = process.env.CTEST_IMAGE_PATH || path.resolve('images'); var test_case = process.env.GEOJS_TEST_CASE || 'tests/all.js'; var getRawBody = require('raw-body'); @@ -10,10 +11,97 @@ var getRawBody = require('raw-body'); if (!fs.existsSync(notes_path)) { fs.mkdirSync(notes_path); } +if (!fs.existsSync(image_path)) { + fs.mkdirSync(image_path); +} /** - * Express style middleware to handle REST requests - * to `/notes` on the test server. + * This function returns true if (1) there is an environment variable set + * called "TEST_SAVE_IMAGE", and either (2a) the name of the test appears in + * the value of the variable when treated as a comma separated list of strings, + * or (2b) the variable reads "all". + */ +function doSaveImage(name) { + var saveList = process.env.TEST_SAVE_IMAGE; + var result; + if (saveList === 'all' || !saveList) { + result = !!saveList; + } else { + result = saveList.split(',').indexOf(name) >= 0; + if (name.indexOf('-') >= 0) { + result = result || saveList.split(',').indexOf(name.split('-')[1]) >= 0; + } + } + return result; +} + +/* Save an image. The image is expected to be a base64 encoded string, with or + * without a mime type specifier. + * + * @param {string} name: base name for the image. + * @param {string} image: a base64 encoded png image. + * @param {boolean} always: if true, always save the image regardless of + * environment settings. + */ +function saveImage(name, image, always) { + if (always || doSaveImage(name)) { + if (image.indexOf(',') >= 0) { + image = image.split(',')[1]; + } + var dest = path.resolve(image_path, name + '.png'); + fs.writeFileSync(dest, image, 'base64'); + } +} + +/* Compare an image to a base image. If it violates a threshold, save the + * image and a diff between it and the base image. Returns the resemble + * results. + * + * @param {string} name: base name for the image. + * @param {string} image: a base64 encoded png image. + * @param {number} threshold: allowed difference between this image and the + * base image. + * @param {function} callback: a function to call when complete. + */ +function compareImage(name, image, threshold, callback) { + /* Note, we could read the xvfb frame buffer using imageMagick, which would + * get the entire browser display, including it's window border, tabs, search + * bar, and non-canvas elements. It might be worth install a kiosk extension + * to FireFox (or use Chrome in Kiosk mode), and exclude the portions of the + * window that are used for Karma information. + var child_process = require('child_process'); + var dest = path.resolve(image_path, name + '-xvfb.png'); + child_process.execSync('import -window root \'' + dest.replace(/'/g, "'\\''") + '\''); + var xvfbImage = new Buffer(fs.readFileSync(dest)).toString('base64'); + xvfbImage = 'data:image/png;base64,' + xvfbImage; + */ + var resemble = require('node-resemble'); + var src = path.resolve('dist/data/base-images', name + '.png'); + if (!fs.existsSync(src)) { + src = path.resolve(image_path, name + '.png'); + } + var refImage = new Buffer(fs.readFileSync(src)).toString('base64'); + refImage = 'data:image/png;base64,' + refImage; + resemble(image) + .compareTo(refImage) + .ignoreAntialiasing() + .onComplete(function (results) { + console.log('Image comparison: ' + name + ', delta: ' + + Number(results.misMatchPercentage) * 0.01); + var passed = (Number(results.misMatchPercentage) <= threshold * 100); + saveImage(name + '-base', refImage, !passed); + saveImage(name + '-test', image, !passed); + saveImage(name + '-diff', results.getImageDataUrl(), !passed); + results.passed = passed; + if (callback) { + callback(results); + } + }); +} + +/** + * Express style middleware to handle REST requests to `/notes` and + * `/testImage` on the test server. */ var notes_middleware = function (config) { var notes = {}; @@ -46,6 +134,32 @@ var notes_middleware = function (config) { response.writeHead(200); return response.end(JSON.stringify(notes)); } + } else if (parsed.pathname === '/testImage') { + if (request.method === 'PUT') { + return getRawBody(request).then(function (body) { + var name = query.name; + var image = '' + body; + saveImage(name, image); + if (query.compare === 'true') { + compareImage(name, image, query.threshold, function (results) { + response.writeHead(200); + return response.end(JSON.stringify(results)); + }); + } else { + response.writeHead(200); + return response.end('{}'); + } + }).catch(function (err) { + response.writeHead(500); + response.end(err.message); + }); + } else if (request.method === 'GET') { + var src = path.resolve(image_path, query.name + '.png'); + var img = new Buffer(fs.readFileSync(src)).toString('base64'); + img = 'data:image/png;base64,' + img; + response.writeHead(200); + return response.end(img); + } } next(); }; @@ -56,7 +170,8 @@ module.exports = { files: [ test_case, {pattern: 'tests/data/**/*', included: false}, - {pattern: 'tests/cases/**/*.js', included: false, served: false, watched: true} + {pattern: 'tests/cases/**/*.js', included: false, served: false, watched: true}, + {pattern: 'tests/gl-cases/**/*.js', included: false, served: false, watched: true} ], proxies: { '/data/': '/base/tests/data/' @@ -64,6 +179,7 @@ module.exports = { browsers: [ 'PhantomJS' ], + browserNoActivityTimeout: 30000, reporters: [ 'progress', 'kjhtml' diff --git a/karma-cov.conf.js b/karma-cov.conf.js index 7d7f4428ec..684ee40ba6 100644 --- a/karma-cov.conf.js +++ b/karma-cov.conf.js @@ -16,6 +16,7 @@ karma_config.coverageReporter = { reporters: [ {type: 'html', dir: 'dist/coverage/', subdir: browser}, {type: 'cobertura', dir: 'dist/cobertura/', file: 'coverage.xml', subdir: browser}, + {type: 'json', dir: 'dist/coverage/json/', subdir: browser}, {type: 'lcovonly', dir: 'lcov', subdir: browser}, {type: 'text'} ] diff --git a/package.json b/package.json index 71debc922c..c1dec27c00 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "gl-vec4": "^1.0.1", "glob": "^7.0.3", "imports-loader": "^0.6.5", + "istanbul-combine": "^0.3.0", "istanbul-instrumenter-loader": "^0.2.0", "jade": "^1.11.0", "jade-loader": "^0.8.0", @@ -52,6 +53,7 @@ "json-loader": "^0.5.4", "karma": "^0.13.22", "karma-coverage": "^1.0.0", + "karma-firefox-launcher": "^1.0.0", "karma-jasmine": "^1.0.2", "karma-jasmine-html-reporter": "^0.2.0", "karma-phantomjs-launcher": "^1.0.0", @@ -60,6 +62,7 @@ "karma-webpack": "^1.7.0", "mousetrap": "^1.6.0", "node-fs-extra": "^0.8.1", + "node-resemble": "^1.1.3", "phantomjs-prebuilt": "^2.1.5", "proj4": "^2.3.14", "raw-body": "^2.1.6", @@ -75,10 +78,13 @@ "build": "webpack --config webpack.config.js && webpack --config external.config.js", "build-examples": "webpack --config webpack-examples.config.js", "lint": "eslint --cache .", - "test": "karma start karma-cov.conf.js --single-run", + "test": "GEOJS_TEST_CASE=tests/test-unit.js karma start karma-cov.conf.js --single-run --browsers PhantomJS", "start": "karma start karma.conf.js", - "ci": "karma start karma-cov.conf.js --single-run --browsers PhantomJS", + "ci": "GEOJS_TEST_CASE=tests/test-unit.js karma start karma-cov.conf.js --single-run --browsers PhantomJS", + "ffci": "GEOJS_TEST_CASE=tests/test-gl.js karma start karma-cov.conf.js --single-run --browsers Firefox", + "test-webgl": "GEOJS_TEST_CASE=tests/test-gl.js xvfb-run -s '-ac -screen 0 1280x1024x24' karma start karma-cov.conf.js --single-run --browsers Firefox", "codecov": "cat lcov/*/lcov.info | codecov", + "combine-coverage": "istanbul-combine -d dist/cobertura -r cobertura 'dist/coverage/json/**/coverage-final.json'", "examples": "webpack-dev-server --config webpack-examples.config.js --host ${HOST-127.0.0.1} --port ${PORT-8082} --content-base dist/", "start-test": "node examples/build.js; forever start ./testing/test-runners/server.js", "stop-test": "forever stop ./testing/test-runners/server.js", diff --git a/src/choroplethFeature.js b/src/choroplethFeature.js index 35eef95f7d..7fd7262e2f 100644 --- a/src/choroplethFeature.js +++ b/src/choroplethFeature.js @@ -73,7 +73,7 @@ var choroplethFeature = function (arg) { var scalarId, scalarValue; if (data === undefined) { - return m_this.choropleth.get('scalar')(); + return m_this.choropleth.get('scalar')() || []; } else { scalarId = m_this.choropleth.get('accessors')().scalarId; scalarValue = m_this.choropleth.get('accessors')().scalarValue; diff --git a/src/gl/vglRenderer.js b/src/gl/vglRenderer.js index 21288d1995..076a5a33b6 100644 --- a/src/gl/vglRenderer.js +++ b/src/gl/vglRenderer.js @@ -90,6 +90,17 @@ var vglRenderer = function (arg) { canvas.attr('class', 'webgl-canvas'); canvas.css('display', 'block'); $(m_this.layer().node().get(0)).append(canvas); + + if (window.contextPreserveDrawingBuffer) { + var elem = canvas.get(0); + var getContext = elem.getContext; + elem.getContext = function (contextType, contextAttributes) { + contextAttributes = contextAttributes || {}; + contextAttributes.preserveDrawingBuffer = true; + return getContext.call(elem, contextType, contextAttributes); + }; + } + m_viewer = vgl.viewer(canvas.get(0), arg.options); m_viewer.init(); m_contextRenderer = m_viewer.renderWindow().activeRenderer(); diff --git a/src/graphFeature.js b/src/graphFeature.js index 86f96994b9..549186da3f 100644 --- a/src/graphFeature.js +++ b/src/graphFeature.js @@ -5,7 +5,7 @@ var feature = require('./feature'); /** * Create a new instance of class graphFeature * - * @class geo.greaphFeature + * @class geo.graphFeature * @extends geo.feature * @returns {geo.graphFeature} */ diff --git a/testing/test-data/base-images.tgz.md5 b/testing/test-data/base-images.tgz.md5 new file mode 100644 index 0000000000..49ed141795 --- /dev/null +++ b/testing/test-data/base-images.tgz.md5 @@ -0,0 +1 @@ +ef2201b45e34c62039654cf9366c83fb \ No newline at end of file diff --git a/testing/test-data/base-images.tgz.url b/testing/test-data/base-images.tgz.url new file mode 100644 index 0000000000..ea4eafbe6b --- /dev/null +++ b/testing/test-data/base-images.tgz.url @@ -0,0 +1 @@ +https://data.kitware.com/api/v1/file/584b12d48d777f1e342887d3/download \ No newline at end of file diff --git a/testing/test-runners/baseline_images.py b/testing/test-runners/baseline_images.py new file mode 100755 index 0000000000..91740a1bbc --- /dev/null +++ b/testing/test-runners/baseline_images.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +import argparse +import girder_client +import md5 +import os +import subprocess +import time + + +def generate_baselines(args): + """ + Generate baseline image tarball by running ctest in the build directory. + + :param args: a dictionary of arguments, including: + xvfb: if True, run ctest within xvfb-run. + build: the build directory; created if it does not exist. + verbose: the verbosity level. + """ + cwd = os.path.abspath(os.path.expanduser(os.getcwd())) + buildPath = os.path.abspath(os.path.expanduser(args.get('build'))) + if args['verbose'] >= 1: + print('Build path: %s' % buildPath) + if not os.path.exists(buildPath): + os.makedirs(buildPath) + if not os.path.isdir(buildPath): + raise Exception('build path is not a directory') + os.chdir(buildPath) + if not os.path.exists(os.path.join(buildPath, 'CMakeFiles')): + cmd = ['cmake', '-D', 'FFHEADLESS_TESTS=ON', cwd] + if args['verbose'] >= 1: + print('Running cmake: %s' % subprocess.list2cmdline(cmd)) + subprocess.check_call(cmd) + tarPath = os.path.join(buildPath, 'base-images.tgz') + if os.path.exists(tarPath): + os.unlink(tarPath) + cmd = ['ctest', '-C', 'baseline_images', '-R', 'baseline_images', + '--output-on-failure'] + if args.get('xvfb'): + cmd = ['xvfb-run', '-s', '-ac -screen 0 1280x1024x24', 'bash', '-c', + subprocess.list2cmdline(cmd)] + if args['verbose'] >= 1: + print('Running ctest: %s' % subprocess.list2cmdline(cmd)) + subprocess.check_call(cmd) + os.chdir(cwd) + tarSize = os.path.getsize(tarPath) + if args['verbose'] >= 1: + print('Created baseline image tgz file, %d bytes' % tarSize) + + +def upload_baselines(args): + """ + Upload the baseline image tarball to a girder instance. + + :param args: a dictionary of arguments, including: + build: the build directory where the tarball is located. + dest: the root url of the Girder instance. + folder: the Girder folder ID to upload to. + apikey: a Girder authentication token. Optional. + username: a Girder username. Optional. + password: a Girder username. Optional. + verbose: the verbosity level. + """ + buildPath = os.path.abspath(os.path.expanduser(args.get('build'))) + tarPath = os.path.join(buildPath, 'base-images.tgz') + tarSize = os.path.getsize(tarPath) + # Get the folder we want to upload to to ensure it exists + apiRoot = args['dest'].rstrip('/') + '/api/v1/' + gc = girder_client.GirderClient(apiUrl=apiRoot) + if args.get('apikey'): + gc.authenticate(apiKey=args.get('apikey')) + elif args.get('username') and args.get('password'): + gc.authenticate(username=args.get('username'), + password=args.get('password')) + else: + gc.authenticate(username=args.get('username'), interactive=True) + name = 'Baseline Images %s.tgz' % time.strftime( + '%Y-%m-%d %H:%M:%S', time.localtime(os.path.getmtime(tarPath))) + uploadedFile = gc.uploadFile( + parentId=args['folder'], parentType='folder', name=name, + stream=open(tarPath), size=tarSize, mimeType='application/tar+gzip') + if args['verbose'] >= 1: + print('Upload to file %s' % uploadedFile['_id']) + testDataPath = os.path.abspath('testing/test-data') + if not os.path.isdir(testDataPath): + raise Exception('Can\'t update test-data information.') + open(os.path.join(testDataPath, 'base-images.tgz.url'), 'w').write( + apiRoot + 'file/%s/download' % uploadedFile['_id']) + open(os.path.join(testDataPath, 'base-images.tgz.md5'), 'w').write( + md5.new(open(tarPath).read()).hexdigest()) + if args['verbose'] >= 1: + print('test-data references updated') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Generate and upload a new set of baseline images for ' + 'headless Firefox testing. This should be run in the root directory ' + 'of the geojs repository.') + parser.add_argument( + '--xvfb', '-x', dest='xvfb', action='store_true', + help='Run xvfb-run when running ctest to generate the baseline ' + 'images. Only applies if the images are generated.') + parser.add_argument( + '--no-xvfb', dest='xvfb', action='store_false', + help='Don\'t use xvfb-run when generating baseline images.') + parser.add_argument( + '--generate', '-g', dest='make', action='store_true', + help='Generate baseline images by running "ctest -C baseline_images ' + '-R baseline_images".') + parser.add_argument( + '--no-generate', dest='make', action='store_false', + help='Don\'t generate baseline images.') + parser.add_argument( + '--build', '-b', default='.', + help='The build directory. This is created if baseline images are ' + 'generated and the directory does not exist.') + parser.add_argument( + '--upload', '-u', dest='upload', action='store_true', + help='Upload base-images.tgz.') + parser.add_argument( + '--no-upload', dest='upload', action='store_false', + help='Don\'t upload the baseline image tarball.') + parser.add_argument( + '--dest', '-d', default='https://data.kitware.com', + help='Destination for upload. Must be a Girder server. /api/v1 is ' + 'added to this destination to reach the Girder api.') + parser.add_argument( + '--folder', '-f', default='5841a0488d777f5cdd826f1b', + help='Destination folder ID.') + parser.add_argument( + '--apikey', '-a', + help='Girder API key. If neither an API key nor a username and ' + 'password are given, an interactive prompt requests a username and ' + 'password.') + parser.add_argument( + '--username', '--user', help='Girder username. Optional.') + parser.add_argument( + '--password', '--pass', help='Girder password. Optional.') + parser.add_argument('--verbose', '-v', action='count', default=0) + + args = vars(parser.parse_args()) + if args['verbose'] >= 2: + print('Parsed arguments: %r' % args) + if args.get('make'): + generate_baselines(args) + if args.get('upload'): + upload_baselines(args) diff --git a/tests/all.js b/tests/all.js index 25257e9099..a1b5e177cd 100644 --- a/tests/all.js +++ b/tests/all.js @@ -1,7 +1,9 @@ /** - * Entry point for all tests in ./cases/* - * This is here to prevent webpack from create a seperate bundle for each - * test case. See: https://github.com/webpack/karma-webpack/issues/23 + * Entry point for all tests. */ -var tests = require.context('./cases', true, /.*\.js$/); + +var tests; +tests = require.context('./cases', true, /.*\.js$/); +tests.keys().forEach(tests); +tests = require.context('./gl-cases', true, /.*\.js$/); tests.keys().forEach(tests); diff --git a/tests/gl-cases/choropleth.js b/tests/gl-cases/choropleth.js new file mode 100644 index 0000000000..09e4a8af2f --- /dev/null +++ b/tests/gl-cases/choropleth.js @@ -0,0 +1,74 @@ +describe('choropleth', function () { + var imageTest = require('../image-test'); + var common = require('../test-common'); + + var states = [ + { + 'type': 'Feature', + 'properties': { + 'GEO_ID': '0400000US04', + 'STATE': '04', + 'NAME': 'Arizona', + 'LSAD': '', + 'CENSUSAREA': 113594.084000 + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[[-112.538593, 37.000674], [-112.534545, 37.000684], [-112.368946, 37.001125], [-112.357690, 37.001025], [-111.412784, 37.001478], [-111.405869, 37.001481], [-111.405517, 37.001497], [-111.189888, 37.000959], [-110.750690, 37.003197], [-110.495259, 37.003875], [-110.331050, 36.998216], [-110.000677, 36.997968], [-109.381226, 36.999148], [-109.378039, 36.999135], [-109.270097, 36.999266], [-109.268213, 36.999242], [-109.263390, 36.999263], [-109.246917, 36.999346], [-109.233848, 36.999266], [-109.181196, 36.999271], [-109.045223, 36.999084], [-109.045244, 36.969489], [-109.045272, 36.968871], [-109.045407, 36.874998], [-109.045433, 36.874589], [-109.045973, 36.002338], [-109.046011, 35.925896], [-109.046054, 35.925860], [-109.046055, 35.888721], [-109.046024, 35.879800], [-109.046295, 35.616517], [-109.046296, 35.614251], [-109.046509, 35.546440], [-109.046481, 35.546326], [-109.046082, 35.174665], [-109.045851, 34.959718], [-109.046156, 34.579291], [-109.046182, 34.522553], [-109.046182, 34.522393], [-109.046627, 33.778233], [-109.046870, 33.372654], [-109.047045, 33.369280], [-109.046909, 33.365570], [-109.046827, 33.365272], [-109.047237, 33.208965], [-109.047116, 33.137995], [-109.047117, 33.137559], [-109.047013, 33.092917], [-109.046905, 33.091931], [-109.047453, 33.069427], [-109.047480, 33.068420], [-109.047117, 32.777570], [-109.047117, 32.777569], [-109.047638, 32.693439], [-109.047645, 32.689988], [-109.047653, 32.686327], [-109.047653, 32.681379], [-109.047926, 32.426376], [-109.048286, 32.089114], [-109.048296, 32.084093], [-109.048731, 32.028174], [-109.048599, 32.013651], [-109.048590, 31.870791], [-109.048769, 31.861383], [-109.048763, 31.810776], [-109.049195, 31.796551], [-109.049813, 31.499528], [-109.049843, 31.499515], [-109.050044, 31.332502], [-109.426931, 31.334033], [-110.460172, 31.333051], [-110.760997, 31.332765], [-110.976828, 31.332560], [-111.098097, 31.339836], [-111.366635, 31.425880], [-111.579530, 31.494095], [-112.246102, 31.704195], [-112.867074, 31.895488], [-113.125961, 31.972780], [-113.333794, 32.038521], [-113.493196, 32.088943], [-114.250775, 32.323910], [-114.813613, 32.494277], [-114.809393, 32.617119], [-114.807390, 32.621332], [-114.799302, 32.625115], [-114.781872, 32.625050], [-114.753111, 32.658304], [-114.748000, 32.664184], [-114.719633, 32.718763], [-114.678632, 32.736614], [-114.677091, 32.736218], [-114.658840, 32.733830], [-114.658260, 32.733799], [-114.615585, 32.728446], [-114.615733, 32.729427], [-114.496827, 32.822119], [-114.496284, 32.822326], [-114.465715, 32.879191], [-114.465715, 32.879420], [-114.462890, 32.905797], [-114.468605, 32.971649], [-114.511343, 33.023455], [-114.516912, 33.026871], [-114.523578, 33.030961], [-114.571653, 33.036624], [-114.578287, 33.035375], [-114.584765, 33.028231], [-114.589778, 33.026228], [-114.606282, 33.025703], [-114.623870, 33.028720], [-114.675104, 33.047532], [-114.707819, 33.091102], [-114.706175, 33.105335], [-114.696829, 33.131209], [-114.687074, 33.142196], [-114.679359, 33.159519], [-114.675360, 33.185489], [-114.672088, 33.258499], [-114.700103, 33.341045], [-114.721233, 33.396912], [-114.665278, 33.415358], [-114.627125, 33.433554], [-114.622918, 33.456561], [-114.594534, 33.495059], [-114.558898, 33.531819], [-114.535664, 33.568788], [-114.535965, 33.569154], [-114.540300, 33.580615], [-114.540617, 33.591412], [-114.530348, 33.679245], [-114.523959, 33.685879], [-114.519113, 33.688473], [-114.496489, 33.696901], [-114.494197, 33.707922], [-114.504340, 33.756381], [-114.523365, 33.806120], [-114.525539, 33.838614], [-114.518434, 33.917518], [-114.499883, 33.961789], [-114.463610, 33.993431], [-114.438266, 34.022609], [-114.434949, 34.037784], [-114.435429, 34.079727], [-114.433380, 34.088413], [-114.420499, 34.103466], [-114.411681, 34.110031], [-114.401352, 34.111652], [-114.390565, 34.110084], [-114.366521, 34.118575], [-114.321618, 34.138093], [-114.312592, 34.144453], [-114.244191, 34.179625], [-114.225861, 34.201774], [-114.164476, 34.251667], [-114.133264, 34.258462], [-114.131489, 34.260387], [-114.138282, 34.303230], [-114.176909, 34.349306], [-114.199482, 34.361373], [-114.264317, 34.401329], [-114.339627, 34.451435], [-114.452547, 34.653494], [-114.465607, 34.692260], [-114.552682, 34.766871], [-114.633051, 34.869971], [-114.635176, 34.875003], [-114.636725, 34.889107], [-114.630877, 34.907263], [-114.629015, 34.986148], [-114.629190, 34.991887], [-114.633013, 35.002085], [-114.633544, 35.015644], [-114.635469, 35.028266], [-114.621956, 35.094678], [-114.599120, 35.121050], [-114.584300, 35.124999], [-114.578524, 35.128750], [-114.572747, 35.138725], [-114.569569, 35.163053], [-114.569238, 35.183480], [-114.595931, 35.325234], [-114.604314, 35.353584], [-114.627137, 35.409504], [-114.652005, 35.429165], [-114.662125, 35.444241], [-114.677643, 35.489742], [-114.676733, 35.499063], [-114.674981, 35.510564], [-114.671147, 35.520641], [-114.653806, 35.599490], [-114.653406, 35.610789], [-114.657506, 35.618289], [-114.676707, 35.640989], [-114.689407, 35.651412], [-114.700308, 35.700387], [-114.707710, 35.811885], [-114.705611, 35.848884], [-114.704211, 35.851984], [-114.706130, 35.878712], [-114.731296, 35.945157], [-114.743576, 35.983721], [-114.755618, 36.087166], [-114.744857, 36.098693], [-114.736165, 36.104367], [-114.631716, 36.142306], [-114.572031, 36.151610], [-114.513256, 36.151217], [-114.381446, 36.141665], [-114.365835, 36.133722], [-114.328777, 36.105501], [-114.310857, 36.083787], [-114.307235, 36.076544], [-114.303614, 36.066456], [-114.292663, 36.051118], [-114.263146, 36.025937], [-114.252651, 36.020193], [-114.238799, 36.014561], [-114.233289, 36.014289], [-114.213690, 36.015613], [-114.154130, 36.023862], [-114.148774, 36.027310], [-114.138202, 36.041284], [-114.125796, 36.077531], [-114.120193, 36.102228], [-114.063021, 36.186942], [-114.060302, 36.189363], [-114.046838, 36.194069], [-114.047106, 36.250591], [-114.048515, 36.289598], [-114.046403, 36.371873], [-114.046488, 36.473449], [-114.050562, 36.656259], [-114.050578, 36.800173], [-114.050583, 36.843141], [-114.050600, 37.000396], [-113.965907, 37.000025], [-113.965907, 36.999976], [-112.966471, 37.000219], [-112.899366, 37.000319], [-112.609787, 37.000753], [-112.558974, 37.000692], [-112.545094, 37.000734], [-112.540368, 37.000669], [-112.538593, 37.000674]]] + } + }, + { + 'type': 'Feature', + 'properties': { + 'GEO_ID': '0400000US05', + 'STATE': '05', + 'NAME': 'Arkansas', + 'LSAD': '', + 'CENSUSAREA': 52035.477000 + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[[-94.042964, 33.019219], [-94.043036, 33.079485], [-94.042870, 33.092727], [-94.043007, 33.133890], [-94.043077, 33.138162], [-94.043185, 33.143476], [-94.042875, 33.199785], [-94.042892, 33.202666], [-94.042876, 33.215219], [-94.042730, 33.241823], [-94.043004, 33.250128], [-94.043050, 33.260904], [-94.042990, 33.271227], [-94.042990, 33.271243], [-94.043067, 33.330498], [-94.043067, 33.347351], [-94.043067, 33.352097], [-94.043128, 33.358757], [-94.042869, 33.371170], [-94.043053, 33.377716], [-94.042887, 33.420225], [-94.042988, 33.431024], [-94.042988, 33.435824], [-94.043188, 33.470324], [-94.043279, 33.491030], [-94.043009, 33.493039], [-94.043375, 33.542315], [-94.043428, 33.551425], [-94.043450, 33.552253], [-94.066846, 33.568909], [-94.085243, 33.575546], [-94.181805, 33.593217], [-94.252656, 33.586144], [-94.257801, 33.582508], [-94.355945, 33.543180], [-94.409329, 33.568265], [-94.464336, 33.598819], [-94.470963, 33.605940], [-94.478366, 33.620847], [-94.485875, 33.637867], [-94.485577, 33.653310], [-94.485528, 33.663388], [-94.484520, 33.687909], [-94.484616, 33.691592], [-94.483840, 33.711332], [-94.483874, 33.716733], [-94.482870, 33.750564], [-94.482862, 33.750780], [-94.482777, 33.753638], [-94.482682, 33.756286], [-94.481842, 33.789008], [-94.481543, 33.795719], [-94.481361, 33.802649], [-94.481355, 33.802887], [-94.480574, 33.830166], [-94.479954, 33.851330], [-94.478994, 33.881197], [-94.478842, 33.881485], [-94.477387, 33.937759], [-94.477318, 33.940932], [-94.477038, 33.953838], [-94.476957, 33.957365], [-94.474895, 34.019655], [-94.474896, 34.021838], [-94.474896, 34.021877], [-94.470292, 34.189864], [-94.465847, 34.352073], [-94.465425, 34.359548], [-94.464176, 34.402713], [-94.463816, 34.414465], [-94.463671, 34.419585], [-94.461149, 34.507457], [-94.460058, 34.545264], [-94.460052, 34.547869], [-94.457500, 34.634945], [-94.457530, 34.642961], [-94.454576, 34.728962], [-94.450233, 34.855413], [-94.450140, 34.858694], [-94.450065, 34.861335], [-94.449630, 34.875253], [-94.449058, 34.890556], [-94.449086, 34.894152], [-94.449253, 34.895869], [-94.447889, 34.933941], [-94.441232, 35.119724], [-94.440754, 35.128806], [-94.439550, 35.169037], [-94.439509, 35.171807], [-94.439056, 35.193588], [-94.439084, 35.197298], [-94.438470, 35.208587], [-94.438247, 35.210992], [-94.437774, 35.239271], [-94.437578, 35.242202], [-94.435812, 35.271300], [-94.435706, 35.274267], [-94.435316, 35.275893], [-94.435280, 35.287485], [-94.435170, 35.291494], [-94.434115, 35.306493], [-94.431815, 35.362891], [-94.432685, 35.380806], [-94.433742, 35.386467], [-94.433915, 35.387391], [-94.431215, 35.394290], [-94.431789, 35.397659], [-94.463318, 35.582660], [-94.464097, 35.587265], [-94.464457, 35.588909], [-94.465272, 35.594037], [-94.472647, 35.638556], [-94.487585, 35.726147], [-94.488210, 35.729240], [-94.492932, 35.759166], [-94.493362, 35.761892], [-94.494549, 35.768303], [-94.499045, 35.793460], [-94.499647, 35.796910], [-94.500526, 35.802642], [-94.500764, 35.803820], [-94.501162, 35.806430], [-94.503011, 35.817210], [-94.504438, 35.826369], [-94.505642, 35.833628], [-94.507631, 35.845901], [-94.522658, 35.934250], [-94.522658, 35.934799], [-94.522634, 35.934892], [-94.522910, 35.936127], [-94.524344, 35.944050], [-94.524640, 35.945727], [-94.528162, 35.965665], [-94.528305, 35.966054], [-94.532071, 35.987852], [-94.533646, 35.996804], [-94.534852, 36.002678], [-94.535724, 36.007807], [-94.547715, 36.077271], [-94.547871, 36.078281], [-94.552184, 36.102235], [-94.561165, 36.152110], [-94.562803, 36.161749], [-94.562828, 36.161895], [-94.565655, 36.178439], [-94.566588, 36.183774], [-94.571253, 36.210901], [-94.571806, 36.213748], [-94.574395, 36.229996], [-94.574880, 36.232741], [-94.575071, 36.233682], [-94.576003, 36.240070], [-94.577899, 36.249548], [-94.577883, 36.250080], [-94.586200, 36.299969], [-94.593397, 36.345742], [-94.599723, 36.387587], [-94.601984, 36.402120], [-94.602623, 36.405283], [-94.605408, 36.421949], [-94.611609, 36.461528], [-94.613830, 36.476248], [-94.615311, 36.484992], [-94.617919, 36.499414], [-94.559290, 36.499496], [-94.519478, 36.499214], [-94.111473, 36.498597], [-94.110673, 36.498587], [-94.100252, 36.498670], [-94.098588, 36.498676], [-94.077089, 36.498730], [-93.963920, 36.498717], [-93.959190, 36.498717], [-93.921840, 36.498718], [-93.906128, 36.498718], [-93.866758, 36.498789], [-93.728022, 36.499037], [-93.727552, 36.499055], [-93.718893, 36.499178], [-93.709956, 36.499179], [-93.700171, 36.499135], [-93.584282, 36.498896], [-93.584281, 36.498896], [-93.514512, 36.498881], [-93.507408, 36.498911], [-93.426989, 36.498585], [-93.396079, 36.498669], [-93.394718, 36.498519], [-93.315337, 36.498408], [-93.315324, 36.498408], [-93.296117, 36.498389], [-93.088988, 36.498184], [-93.087635, 36.498239], [-93.069512, 36.498242], [-93.068455, 36.498250], [-93.013742, 36.498130], [-92.894336, 36.497867], [-92.894001, 36.497850], [-92.854049, 36.497983], [-92.838876, 36.498033], [-92.838621, 36.498079], [-92.772341, 36.497772], [-92.772333, 36.497772], [-92.529145, 36.497739], [-92.318415, 36.497711], [-92.309424, 36.497894], [-92.216412, 36.498417], [-92.214143, 36.498372], [-92.211449, 36.498395], [-92.199396, 36.498351], [-92.150295, 36.498634], [-92.137741, 36.498706], [-92.120415, 36.498863], [-92.120306, 36.498864], [-92.098356, 36.498803], [-92.074934, 36.498761], [-92.057178, 36.498670], [-92.055789, 36.498670], [-92.028847, 36.498642], [-92.019375, 36.498524], [-91.988751, 36.498498], [-91.985802, 36.498431], [-91.865995, 36.498783], [-91.864385, 36.498789], [-91.805981, 36.498987], [-91.802040, 36.498963], [-91.799500, 36.498952], [-91.784713, 36.499074], [-91.766111, 36.499114], [-91.726663, 36.499209], [-91.687615, 36.499397], [-91.686026, 36.499374], [-91.672343, 36.499463], [-91.642590, 36.499335], [-91.631439, 36.499198], [-91.601317, 36.499343], [-91.596213, 36.499162], [-91.549163, 36.499161], [-91.539359, 36.499116], [-91.536870, 36.499156], [-91.529774, 36.499022], [-91.450005, 36.497538], [-91.446284, 36.497469], [-91.436502, 36.497377], [-91.433298, 36.497262], [-91.407261, 36.497123], [-91.407137, 36.497112], [-91.405141, 36.497165], [-91.404915, 36.497120], [-91.227398, 36.497617], [-91.218645, 36.497564], [-91.217360, 36.497511], [-91.126529, 36.497712], [-91.096277, 36.497893], [-91.095880, 36.497870], [-91.017974, 36.498062], [-91.008558, 36.498270], [-90.963063, 36.498418], [-90.960648, 36.498426], [-90.879220, 36.498378], [-90.876867, 36.498423], [-90.876567, 36.498313], [-90.873775, 36.498074], [-90.850434, 36.498548], [-90.784398, 36.498524], [-90.782454, 36.498523], [-90.765672, 36.498494], [-90.711226, 36.498318], [-90.693005, 36.498510], [-90.653246, 36.498488], [-90.648494, 36.498447], [-90.612554, 36.498559], [-90.605450, 36.498459], [-90.594300, 36.498459], [-90.585342, 36.498497], [-90.576180, 36.498446], [-90.576112, 36.498446], [-90.500160, 36.498399], [-90.495027, 36.498371], [-90.494575, 36.498368], [-90.339892, 36.498213], [-90.228943, 36.497771], [-90.220732, 36.497858], [-90.220702, 36.497858], [-90.217323, 36.497797], [-90.193943, 36.497823], [-90.152481, 36.497952], [-90.154409, 36.496832], [-90.156369, 36.487748], [-90.133993, 36.437906], [-90.072897, 36.393007], [-90.064514, 36.382085], [-90.063980, 36.303038], [-90.076301, 36.280708], [-90.083731, 36.272332], [-90.114922, 36.265595], [-90.198735, 36.201382], [-90.220425, 36.184764], [-90.319168, 36.089976], [-90.364430, 36.013625], [-90.368718, 35.995812], [-90.342616, 35.995895], [-90.339434, 35.996033], [-90.292376, 35.996397], [-90.288947, 35.996418], [-90.288800, 35.996419], [-90.158812, 35.997375], [-90.127331, 35.997635], [-90.126350, 35.997596], [-89.972563, 35.998994], [-89.965327, 35.998813], [-89.961075, 35.999135], [-89.959893, 35.999020], [-89.959377, 35.999020], [-89.959375, 35.999020], [-89.901183, 35.999365], [-89.896508, 35.999432], [-89.875586, 35.999562], [-89.875085, 35.999578], [-89.874590, 35.999575], [-89.869010, 35.999640], [-89.770255, 36.000524], [-89.769973, 36.000536], [-89.737648, 36.000567], [-89.737564, 36.000522], [-89.733095, 36.000608], [-89.719970, 35.974620], [-89.686924, 35.947716], [-89.687939, 35.905384], [-89.688141, 35.896946], [-89.714934, 35.906247], [-89.733610, 35.904699], [-89.756036, 35.896817], [-89.765689, 35.891299], [-89.771726, 35.879724], [-89.773564, 35.871697], [-89.772467, 35.865098], [-89.769413, 35.861558], [-89.749424, 35.852955], [-89.729517, 35.847632], [-89.709261, 35.838911], [-89.702883, 35.834153], [-89.701045, 35.828227], [-89.703875, 35.820281], [-89.821216, 35.756716], [-89.865631, 35.746577], [-89.889023, 35.750558], [-89.905538, 35.759063], [-89.909996, 35.759396], [-89.950278, 35.738493], [-89.956254, 35.733386], [-89.958882, 35.723834], [-89.955753, 35.690621], [-89.937383, 35.665711], [-89.922749, 35.655293], [-89.906147, 35.651145], [-89.890510, 35.652408], [-89.884932, 35.655107], [-89.909797, 35.537914], [-89.933572, 35.533299], [-89.957347, 35.528683], [-90.017312, 35.555996], [-90.028620, 35.555249], [-90.032938, 35.553440], [-90.039744, 35.548041], [-90.034976, 35.480705], [-90.034014, 35.468821], [-90.047680, 35.459255], [-90.059068, 35.457889], [-90.067798, 35.466224], [-90.072154, 35.470752], [-90.074420, 35.472518], [-90.107723, 35.476935], [-90.152386, 35.436789], [-90.169002, 35.421853], [-90.179265, 35.385194], [-90.178341, 35.382092], [-90.166246, 35.374745], [-90.143633, 35.374745], [-90.135510, 35.376668], [-90.093589, 35.393333], [-90.089612, 35.379842], [-90.086691, 35.369935], [-90.114893, 35.303887], [-90.153394, 35.302588], [-90.158913, 35.300637], [-90.163812, 35.296115], [-90.168871, 35.281997], [-90.166594, 35.274588], [-90.158865, 35.262577], [-90.152094, 35.255989], [-90.140394, 35.252289], [-90.116493, 35.255788], [-90.105093, 35.254288], [-90.097947, 35.249983], [-90.077410, 35.225479], [-90.074262, 35.218316], [-90.073354, 35.211004], [-90.064612, 35.140621], [-90.065392, 35.137691], [-90.083420, 35.121670], [-90.090610, 35.118287], [-90.100593, 35.116691], [-90.109393, 35.118891], [-90.142794, 35.135091], [-90.160058, 35.128830], [-90.174594, 35.116682], [-90.193859, 35.061646], [-90.295596, 35.040093], [-90.309877, 35.009750], [-90.309297, 34.995694], [-90.253969, 34.954988], [-90.244476, 34.937596], [-90.250095, 34.907320], [-90.293918, 34.860563], [-90.307384, 34.846195], [-90.407964, 34.832767], [-90.414864, 34.831846], [-90.423879, 34.834606], [-90.436431, 34.855060], [-90.461451, 34.856728], [-90.473961, 34.852558], [-90.478131, 34.832542], [-90.467289, 34.782502], [-90.479799, 34.769158], [-90.494811, 34.767490], [-90.501325, 34.769931], [-90.519831, 34.782502], [-90.537345, 34.784170], [-90.549855, 34.763320], [-90.554859, 34.727458], [-90.533175, 34.707442], [-90.509823, 34.689928], [-90.508989, 34.679086], [-90.521499, 34.663240], [-90.539013, 34.659070], [-90.549855, 34.662406], [-90.564033, 34.665742], [-90.576543, 34.657402], [-90.575786, 34.641749], [-90.570133, 34.524829], [-90.565809, 34.435400], [-90.575336, 34.415152], [-90.613944, 34.390723], [-90.658542, 34.375705], [-90.764143, 34.363396], [-90.856367, 34.238928], [-90.868775, 34.228245], [-90.894744, 34.229977], [-90.911480, 34.223051], [-90.913212, 34.210355], [-90.895898, 34.191888], [-90.880894, 34.180346], [-90.880317, 34.169381], [-90.893013, 34.155531], [-90.918405, 34.154376], [-90.934564, 34.130138], [-90.933116, 34.124488], [-90.931969, 34.120017], [-90.928793, 34.107631], [-90.912057, 34.095512], [-90.888396, 34.087433], [-90.879163, 34.065503], [-90.887413, 34.032505], [-90.964708, 34.007952], [-91.018890, 34.003151], [-91.089119, 33.972653], [-91.088164, 33.960078], [-91.036821, 33.914656], [-91.067511, 33.840443], [-91.139869, 33.777117], [-91.144682, 33.750108], [-91.136118, 33.728632], [-91.126805, 33.707814], [-91.127900, 33.695762], [-91.132831, 33.688092], [-91.139953, 33.683709], [-91.148170, 33.679327], [-91.176110, 33.684257], [-91.185971, 33.679327], [-91.188710, 33.669466], [-91.193093, 33.656866], [-91.186519, 33.645909], [-91.157484, 33.626187], [-91.130445, 33.606034], [-91.134043, 33.594489], [-91.152148, 33.582721], [-91.224121, 33.567369], [-91.228287, 33.559937], [-91.232295, 33.552788], [-91.215671, 33.529423], [-91.125109, 33.472842], [-91.118495, 33.449116], [-91.131885, 33.430063], [-91.184427, 33.419576], [-91.141216, 33.298397], [-91.110561, 33.245930], [-91.085984, 33.221644], [-91.087589, 33.145177], [-91.125656, 33.038276], [-91.157662, 33.011217], [-91.166073, 33.004106], [-91.265018, 33.005084], [-91.284398, 33.005007], [-91.312016, 33.005262], [-91.322506, 33.005341], [-91.325037, 33.005364], [-91.326396, 33.005376], [-91.329767, 33.005421], [-91.333011, 33.005529], [-91.376016, 33.005794], [-91.425466, 33.006016], [-91.435782, 33.006099], [-91.460370, 33.006246], [-91.559494, 33.006840], [-91.572326, 33.006908], [-91.579639, 33.006472], [-91.579802, 33.006518], [-91.609001, 33.006556], [-91.617615, 33.006717], [-91.626670, 33.006639], [-91.950001, 33.007520], [-91.951958, 33.007428], [-92.069105, 33.008163], [-92.222825, 33.009080], [-92.292664, 33.010103], [-92.335893, 33.010349], [-92.362865, 33.010628], [-92.370290, 33.010717], [-92.469762, 33.012010], [-92.501383, 33.012160], [-92.503776, 33.012161], [-92.711289, 33.014307], [-92.715884, 33.014398], [-92.723553, 33.014328], [-92.724743, 33.014347], [-92.724994, 33.014351], [-92.733197, 33.014347], [-92.830798, 33.015661], [-92.844073, 33.016034], [-92.844286, 33.016070], [-92.854167, 33.016132], [-92.867510, 33.016062], [-92.946553, 33.016807], [-92.971137, 33.017192], [-92.988708, 33.017298], [-93.070686, 33.017792], [-93.073167, 33.017898], [-93.081428, 33.017928], [-93.100981, 33.017786], [-93.101443, 33.017740], [-93.154351, 33.017856], [-93.197402, 33.017951], [-93.238607, 33.017992], [-93.308181, 33.018156], [-93.308398, 33.018179], [-93.340353, 33.018337], [-93.377134, 33.018234], [-93.467042, 33.018611], [-93.489506, 33.018443], [-93.490520, 33.018442], [-93.490893, 33.018442], [-93.520971, 33.018616], [-93.520994, 33.018616], [-93.524916, 33.018637], [-93.531499, 33.018643], [-93.804930, 33.019347], [-93.814553, 33.019372], [-94.024475, 33.019207], [-94.027983, 33.019139], [-94.035839, 33.019145], [-94.041444, 33.019188], [-94.042964, 33.019219]]] + } + } + ]; + + var myMap; + + beforeEach(function () { + imageTest.prepareImageTest(); + }); + + afterEach(function () { + myMap.exit(); + }); + + it('choropleth', function (done) { + var mapOptions = { + zoom: 4, + center: { + y: 33.60, + x: -95.00 + } + }; + myMap = common.createOsmMap(mapOptions, {}, true); + + var scalarValues = states + .map(function (feature, index) { + return { + id: feature.properties.GEO_ID, + value: index + }; + }); + + var layer = myMap.createLayer('feature', {features: ['choropleth']}); + + layer.createFeature('choropleth') + .data(states) + .scalar(scalarValues) + .choropleth({}); + myMap.draw(); + + imageTest.imageTest('choropleth', 0.001, done, myMap.onIdle, 0, 2); + }); +}); diff --git a/tests/gl-cases/glLines.js b/tests/gl-cases/glLines.js new file mode 100644 index 0000000000..5c8d0ef58f --- /dev/null +++ b/tests/gl-cases/glLines.js @@ -0,0 +1,65 @@ +describe('glLines', function () { + var imageTest = require('../image-test'); + var common = require('../test-common'); + + var data = [ + {'type': 'Feature', 'properties': {'LINEARID': '110685800599', 'FULLNAME': 'N Midway St', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44966, 31.87798], [-85.44965, 31.87835], [-85.449649, 31.87841], [-85.449649, 31.878528], [-85.44965, 31.879264]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801299', 'FULLNAME': 'N Forsyth Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.13888, 31.892975], [-85.13886, 31.893222], [-85.13884, 31.89348], [-85.13883, 31.89374], [-85.13882, 31.893909]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801253', 'FULLNAME': 'N Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14578, 31.891373], [-85.14578, 31.891491], [-85.1458, 31.89168], [-85.14581, 31.892061], [-85.14583, 31.892229], [-85.14584, 31.89266], [-85.14584, 31.892933], [-85.14584, 31.893126], [-85.14583, 31.893351], [-85.14583, 31.893806], [-85.14585, 31.894073], [-85.14586, 31.89422], [-85.14585, 31.894365], [-85.14586, 31.894643], [-85.14587, 31.89479], [-85.14588, 31.894875], [-85.14589, 31.895292], [-85.14589, 31.895471], [-85.14589, 31.89562], [-85.14589, 31.89588], [-85.1459, 31.89678], [-85.1459, 31.89694], [-85.14591, 31.897065], [-85.14591, 31.897566], [-85.1459, 31.897934], [-85.1459, 31.89829], [-85.1459, 31.898602], [-85.1459, 31.898682], [-85.14591, 31.89928], [-85.14591, 31.89951], [-85.14594, 31.90016], [-85.14597, 31.90132], [-85.14598, 31.901471], [-85.146, 31.90198], [-85.14603, 31.902093], [-85.146063, 31.90221], [-85.14611, 31.902332], [-85.14622, 31.902535], [-85.1463, 31.902658], [-85.14649, 31.902986], [-85.14662, 31.903177], [-85.14681, 31.903419], [-85.14691, 31.90354], [-85.14701, 31.903648], [-85.147089, 31.903724], [-85.14727, 31.903883], [-85.14745, 31.90403], [-85.14778, 31.90427], [-85.14808, 31.904527], [-85.1483, 31.904747], [-85.14845, 31.90492], [-85.1486, 31.9051], [-85.14869, 31.905218], [-85.14886, 31.905469], [-85.14898, 31.90566], [-85.14909, 31.905869], [-85.1492, 31.90613], [-85.14929, 31.906386], [-85.14933, 31.90652], [-85.14938, 31.90675], [-85.14943, 31.906966], [-85.14946, 31.907181], [-85.14948, 31.907398], [-85.14948, 31.907488], [-85.14949, 31.907616], [-85.14948, 31.907907], [-85.14946, 31.90812], [-85.14943, 31.90834], [-85.1492, 31.909604], [-85.14917, 31.909784], [-85.14914, 31.909938], [-85.14908, 31.910215], [-85.14902, 31.910573], [-85.149, 31.910714], [-85.14898, 31.910803], [-85.14894, 31.911078], [-85.14891, 31.911224], [-85.14884, 31.911633], [-85.14879, 31.91196], [-85.14864, 31.912801], [-85.1486, 31.912945], [-85.14858, 31.91309], [-85.14818, 31.91526], [-85.14814, 31.91552], [-85.14799, 31.91631], [-85.14792, 31.916694], [-85.14787, 31.91695], [-85.14782, 31.9172], [-85.14773, 31.917631], [-85.14752, 31.91848], [-85.14748, 31.91863], [-85.14746, 31.9187], [-85.14745, 31.918766], [-85.14734, 31.91912], [-85.14733, 31.919187], [-85.14729, 31.919332], [-85.14704, 31.91996], [-85.14689, 31.92035], [-85.14676, 31.920684], [-85.1467, 31.920849], [-85.14662, 31.921026], [-85.14647, 31.92136], [-85.14631, 31.921703], [-85.14615, 31.922037], [-85.14607, 31.92218], [-85.14581, 31.922661], [-85.1455, 31.92321], [-85.14477, 31.924352], [-85.14466, 31.924503], [-85.14445, 31.924794], [-85.14435, 31.92494], [-85.14411, 31.92524], [-85.14363, 31.925848], [-85.14338, 31.926141], [-85.14261, 31.92701], [-85.14209, 31.927591], [-85.14125, 31.92855], [-85.14101, 31.928839], [-85.14078, 31.929143], [-85.140554, 31.929452], [-85.14034, 31.92977], [-85.14009, 31.930167], [-85.13982, 31.9306], [-85.13937, 31.93129], [-85.13917, 31.93161], [-85.13864, 31.93247], [-85.138166, 31.933225], [-85.13737, 31.93451], [-85.13655, 31.935808], [-85.13634, 31.936126], [-85.13611, 31.936432], [-85.13586, 31.93673], [-85.1356, 31.937016], [-85.13532, 31.937291], [-85.13503, 31.937559], [-85.13444, 31.938079], [-85.13233, 31.939904], [-85.1317, 31.940437], [-85.13151, 31.940611], [-85.13137, 31.940729], [-85.13098, 31.94107], [-85.13016, 31.94178], [-85.13009, 31.941845], [-85.12979, 31.94211], [-85.12951, 31.94238], [-85.12938, 31.94251], [-85.12923, 31.94266], [-85.12896, 31.94294], [-85.12869, 31.94323], [-85.12844, 31.94352], [-85.128192, 31.94382], [-85.12795, 31.94413], [-85.127725, 31.94444], [-85.1275, 31.94475], [-85.12729, 31.945067], [-85.12708, 31.94539], [-85.12689, 31.945716], [-85.12651, 31.946349], [-85.12621, 31.946857], [-85.12562, 31.947841], [-85.12543, 31.948171], [-85.12509, 31.948736], [-85.124933, 31.949003], [-85.12399, 31.95063], [-85.12343, 31.95162], [-85.12321, 31.95205], [-85.12308, 31.952317], [-85.12282, 31.95278], [-85.12246, 31.95344], [-85.12232, 31.95369], [-85.12228, 31.953768], [-85.12194, 31.95433], [-85.12048, 31.957], [-85.12017, 31.95761], [-85.12004, 31.95786], [-85.11998, 31.95801], [-85.11963, 31.95883], [-85.11936, 31.959523], [-85.11911, 31.96022], [-85.11903, 31.960407], [-85.11899, 31.96051], [-85.11897, 31.960563], [-85.11885, 31.96091], [-85.11862, 31.96161], [-85.11851, 31.96195], [-85.11845, 31.96213], [-85.11828, 31.96275], [-85.11823, 31.96291], [-85.11811, 31.963339], [-85.118, 31.96369], [-85.11787, 31.96404], [-85.11656, 31.96761], [-85.11621, 31.96857], [-85.11596, 31.96926], [-85.11585, 31.969618], [-85.115753, 31.969975], [-85.11567, 31.970334], [-85.11561, 31.97069], [-85.11555, 31.97106], [-85.11551, 31.97142], [-85.11542, 31.972148], [-85.11533, 31.97287], [-85.114717, 31.97796], [-85.11464, 31.97869], [-85.11461, 31.979057], [-85.114554, 31.97978], [-85.11453, 31.98015], [-85.11452, 31.98052], [-85.11451, 31.981828], [-85.1145, 31.982566], [-85.11449, 31.983262], [-85.11449, 31.983435], [-85.11448, 31.983801], [-85.11447, 31.98453], [-85.11449, 31.985262], [-85.11451, 31.985629], [-85.11454, 31.985992], [-85.11465, 31.987086], [-85.11469, 31.987612], [-85.11471, 31.98784], [-85.11477, 31.9883], [-85.11492, 31.989892], [-85.11498, 31.99044], [-85.1154, 31.99485], [-85.11562, 31.99703], [-85.11575, 31.99813], [-85.11579, 31.99849], [-85.11582, 31.99885], [-85.11598, 32.00051], [-85.11603, 32.00104], [-85.11609, 32.00154], [-85.116158, 32.00226], [-85.116323, 32.00399], [-85.11642, 32.00495], [-85.11658, 32.006636], [-85.11669, 32.007719], [-85.11672, 32.008082], [-85.11688, 32.009717], [-85.11748, 32.015946], [-85.11763, 32.017542], [-85.11767, 32.017905], [-85.11772, 32.018267], [-85.11779, 32.018628], [-85.11787, 32.018988], [-85.11795, 32.019348], [-85.11804, 32.019705], [-85.11815, 32.02006], [-85.1186, 32.02147], [-85.11883, 32.02217], [-85.119057, 32.022878], [-85.11974, 32.024991], [-85.11982, 32.025207], [-85.120004, 32.025723], [-85.12018, 32.026308], [-85.1203, 32.026663], [-85.12053, 32.027363], [-85.12066, 32.027712], [-85.1208, 32.028056], [-85.1211, 32.028737], [-85.12145, 32.029403], [-85.12153, 32.029533], [-85.12161, 32.029702], [-85.12173, 32.029919], [-85.12192, 32.030242], [-85.12347, 32.03293], [-85.12374, 32.033378], [-85.12465, 32.034967], [-85.12479, 32.035218], [-85.12536, 32.0362], [-85.12558, 32.036583], [-85.12574, 32.036856], [-85.12592, 32.037185], [-85.12631, 32.03784], [-85.12656, 32.03828], [-85.126875, 32.03882], [-85.12933, 32.043078], [-85.13031, 32.044787], [-85.13538, 32.05355], [-85.1369, 32.056176], [-85.13881, 32.05948], [-85.13936, 32.06043], [-85.13975, 32.06108], [-85.14013, 32.06174], [-85.14038, 32.062173]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800601', 'FULLNAME': 'N Midway St', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44965, 31.879264], [-85.44966, 31.88007], [-85.44966, 31.881092], [-85.44964, 31.882305], [-85.44963, 31.883703], [-85.44965, 31.883916], [-85.44972, 31.884046], [-85.44983, 31.884147], [-85.44999, 31.884199], [-85.45015, 31.88421], [-85.45072, 31.88421], [-85.45103, 31.884211], [-85.45222, 31.884232], [-85.45242, 31.884241], [-85.45257, 31.88425], [-85.45282, 31.88425], [-85.45375, 31.88426], [-85.45406, 31.88427], [-85.45526, 31.884274], [-85.4562, 31.88428], [-85.45645, 31.8843], [-85.4568, 31.884328], [-85.45736, 31.884401], [-85.45754, 31.88443], [-85.45783, 31.88448], [-85.45854, 31.8846], [-85.45879, 31.88464], [-85.45898, 31.88467], [-85.4592, 31.88471], [-85.459458, 31.88476], [-85.45971, 31.88482], [-85.46017, 31.88497], [-85.46039, 31.88503], [-85.46069, 31.88515], [-85.4609, 31.885218]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685799471', 'FULLNAME': 'N Main St', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.55372, 31.78642], [-85.55322, 31.78721], [-85.55247, 31.78835], [-85.55202, 31.78903], [-85.55158, 31.789694], [-85.55107, 31.79044], [-85.55095, 31.79063], [-85.55083, 31.79082], [-85.55074, 31.79102], [-85.55069, 31.79116], [-85.55066, 31.79128], [-85.55067, 31.79138], [-85.55064, 31.7916], [-85.55064, 31.79182], [-85.55068, 31.79248], [-85.55078, 31.79379], [-85.55083, 31.79445], [-85.5509, 31.795542], [-85.55092, 31.79576], [-85.55092, 31.795963], [-85.55092, 31.79628], [-85.55089, 31.79654], [-85.55088, 31.7967], [-85.55078, 31.79765], [-85.55077, 31.79772], [-85.55076, 31.79785], [-85.55068, 31.798595], [-85.55067, 31.79873], [-85.55062, 31.79923], [-85.55041, 31.80122], [-85.55034, 31.801874], [-85.5503, 31.80224], [-85.55027, 31.80253], [-85.55017, 31.8034], [-85.55009, 31.80383], [-85.54999, 31.80421], [-85.54997, 31.80426], [-85.54982, 31.80468], [-85.54963, 31.805091], [-85.54941, 31.805485], [-85.54916, 31.805866], [-85.54887, 31.80623], [-85.54879, 31.80632], [-85.54856, 31.80658], [-85.54822, 31.80691], [-85.54786, 31.807216], [-85.54746, 31.8075], [-85.54705, 31.80776], [-85.54662, 31.808], [-85.54617, 31.80821], [-85.54562, 31.80842], [-85.54543, 31.808492]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685802080', 'FULLNAME': 'N Randolph Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14399, 31.89295], [-85.144, 31.893158], [-85.144, 31.893427], [-85.14404, 31.894557], [-85.14405, 31.8953], [-85.14405, 31.895853], [-85.14406, 31.897104], [-85.14406, 31.8973], [-85.14406, 31.897371], [-85.14406, 31.898323], [-85.14412, 31.901977], [-85.14412, 31.902186], [-85.14402, 31.902336], [-85.14392, 31.90221], [-85.14391, 31.901983], [-85.14391, 31.901789], [-85.143903, 31.900629], [-85.14387, 31.89831], [-85.14386, 31.89738], [-85.14385, 31.89725], [-85.14386, 31.897102], [-85.14383, 31.895858], [-85.14383, 31.895244], [-85.14382, 31.894564], [-85.14383, 31.893962], [-85.14384, 31.893885], [-85.14385, 31.893595], [-85.14383, 31.89316], [-85.14383, 31.892945], [-85.14382, 31.891973], [-85.14381, 31.89152], [-85.1438, 31.8914]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800668', 'FULLNAME': 'N Orange Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14211, 31.89459], [-85.14212, 31.894835], [-85.14212, 31.89498], [-85.14211, 31.89524], [-85.1421, 31.895558], [-85.1421, 31.895889], [-85.14211, 31.89632], [-85.14211, 31.896511], [-85.14211, 31.897369], [-85.1421, 31.898472], [-85.14209, 31.899086], [-85.14208, 31.89933], [-85.14208, 31.89943], [-85.14207, 31.899838], [-85.142072, 31.9003], [-85.142072, 31.900603], [-85.14207, 31.90107], [-85.14208, 31.901142], [-85.14211, 31.90121], [-85.14216, 31.901267], [-85.1422, 31.9013]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800669', 'FULLNAME': 'N Orange Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14219, 31.89296], [-85.14219, 31.893173], [-85.14218, 31.89349], [-85.14218, 31.893637], [-85.14217, 31.894472], [-85.14211, 31.89459]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800245', 'FULLNAME': 'N Armory St', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44422, 31.87817], [-85.44421, 31.879314], [-85.44418, 31.8806], [-85.44419, 31.88079], [-85.44416, 31.882102], [-85.44417, 31.882188], [-85.44419, 31.882257], [-85.44422, 31.88231], [-85.44427, 31.88238]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801300', 'FULLNAME': 'N Forsyth Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.13881, 31.891449], [-85.13881, 31.891572], [-85.13882, 31.892216], [-85.13882, 31.892363], [-85.13884, 31.89282], [-85.13888, 31.892975]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685802791', 'FULLNAME': 'N Livingston Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.1407, 31.891434], [-85.14073, 31.891561], [-85.14074, 31.891753], [-85.14076, 31.892764], [-85.14074, 31.892981], [-85.14073, 31.893186], [-85.14071, 31.89356], [-85.14069, 31.89457], [-85.14069, 31.894641]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801301', 'FULLNAME': 'N Forsyth Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.13895, 31.89145], [-85.13894, 31.891573], [-85.13895, 31.892189], [-85.13895, 31.892575], [-85.13894, 31.892809], [-85.13888, 31.892975]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685799403', 'FULLNAME': 'N Keen St', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44104, 31.879337], [-85.44095, 31.880336], [-85.44094, 31.880557]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685799592', 'FULLNAME': 'N Witt Ln', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44839, 31.879268], [-85.44839, 31.879097], [-85.4484, 31.879025], [-85.4484, 31.878952], [-85.4484, 31.878447], [-85.4484, 31.878146], [-85.44839, 31.877769], [-85.44839, 31.877592], [-85.44839, 31.877321]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801252', 'FULLNAME': 'N Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14563, 31.89138], [-85.14564, 31.89149], [-85.14564, 31.89165], [-85.14565, 31.8918], [-85.14566, 31.892936], [-85.14566, 31.893137], [-85.14566, 31.89392], [-85.14565, 31.8945], [-85.14566, 31.894578], [-85.145648, 31.8948], [-85.14565, 31.89529], [-85.14566, 31.89562], [-85.14568, 31.896552], [-85.14569, 31.897067], [-85.14572, 31.898291], [-85.14573, 31.89875], [-85.1458, 31.90121], [-85.14582, 31.901875], [-85.14584, 31.902019], [-85.14586, 31.90208], [-85.14591, 31.902217], [-85.14596, 31.902331], [-85.146104, 31.902609], [-85.14632, 31.903005], [-85.14657, 31.90338], [-85.14671, 31.90356], [-85.14688, 31.90375], [-85.146982, 31.90384], [-85.1471, 31.903946], [-85.14722, 31.904048], [-85.14735, 31.90415], [-85.14755, 31.904286], [-85.14774, 31.904432], [-85.14797, 31.90463], [-85.148102, 31.904755], [-85.14837, 31.905042], [-85.14851, 31.90522], [-85.14869, 31.905473], [-85.14881, 31.905667], [-85.14891, 31.90585], [-85.14902, 31.90607], [-85.1491, 31.906279], [-85.14917, 31.906491], [-85.14919, 31.906583], [-85.14928, 31.906919], [-85.14934, 31.907426], [-85.14934, 31.907792], [-85.14932, 31.90801], [-85.14921, 31.908732], [-85.14905, 31.909521], [-85.14881, 31.910772], [-85.14873, 31.911314], [-85.14867, 31.911649], [-85.14865, 31.91176], [-85.14862, 31.911909], [-85.14854, 31.91226], [-85.14842, 31.91296], [-85.14833, 31.913475], [-85.14796, 31.91551], [-85.14784, 31.91607], [-85.1478, 31.91628], [-85.147684, 31.916948], [-85.1476, 31.91743], [-85.147515, 31.917788], [-85.14735, 31.91843], [-85.14733, 31.91849], [-85.14731, 31.91857], [-85.14707, 31.91934], [-85.14684, 31.91991], [-85.14664, 31.92049], [-85.14651, 31.920805], [-85.14637, 31.92114], [-85.14605, 31.921818], [-85.14587, 31.92215], [-85.14585, 31.92218], [-85.14561, 31.922609], [-85.14462, 31.924201], [-85.14441, 31.9245], [-85.14427, 31.92469], [-85.14402, 31.92503], [-85.14378, 31.92533], [-85.14354, 31.92563], [-85.14303, 31.92622], [-85.14122, 31.92826], [-85.14097, 31.92855], [-85.14073, 31.92886], [-85.1405, 31.929165], [-85.14028, 31.92948], [-85.14007, 31.929793], [-85.13966, 31.930433], [-85.13862, 31.932112], [-85.13843, 31.93241], [-85.13722, 31.934342], [-85.13665, 31.93527], [-85.13624, 31.935907], [-85.13601, 31.936218], [-85.13577, 31.936521], [-85.13551, 31.936812], [-85.13524, 31.937092], [-85.13495, 31.937361], [-85.13465, 31.937623], [-85.13285, 31.93918], [-85.13195, 31.939957], [-85.1312, 31.940599], [-85.13084, 31.940919], [-85.13014, 31.94152], [-85.12996, 31.94168], [-85.12984, 31.94178], [-85.12955, 31.94205], [-85.12927, 31.942324], [-85.12919, 31.942399], [-85.12899, 31.9426], [-85.12872, 31.94289], [-85.12846, 31.943173], [-85.12821, 31.94347], [-85.127967, 31.943767], [-85.12773, 31.94407], [-85.1275, 31.94438], [-85.12728, 31.94469], [-85.12707, 31.94501], [-85.12667, 31.945653], [-85.1257, 31.947278], [-85.12489, 31.948654], [-85.12415, 31.94989], [-85.12378, 31.95054], [-85.12295, 31.95204], [-85.12232, 31.95319], [-85.12213, 31.953517], [-85.12193, 31.95384], [-85.12172, 31.95424], [-85.12149, 31.954715], [-85.12004, 31.957364], [-85.1197, 31.95802], [-85.11941, 31.958719], [-85.11927, 31.959065], [-85.11876, 31.96046], [-85.11874, 31.960509], [-85.11865, 31.96075], [-85.11853, 31.961102], [-85.11841, 31.96145], [-85.11828, 31.961799], [-85.11821, 31.962078], [-85.1181, 31.962586], [-85.11791, 31.9633], [-85.11781, 31.963655], [-85.1177, 31.96401], [-85.11758, 31.96436], [-85.11707, 31.965755], [-85.11656, 31.967148], [-85.1163, 31.96785], [-85.11604, 31.968543], [-85.11592, 31.96889], [-85.1158, 31.969242], [-85.11568, 31.969594], [-85.11559, 31.969948], [-85.115505, 31.970308], [-85.11544, 31.970667], [-85.11539, 31.97103], [-85.11508, 31.973574], [-85.11482, 31.97576], [-85.11473, 31.976484], [-85.11464, 31.97721], [-85.11452, 31.978301], [-85.114443, 31.979029], [-85.11441, 31.97939], [-85.11437, 31.980122], [-85.11435, 31.980853], [-85.11433, 31.98176], [-85.11432, 31.982312], [-85.11432, 31.982587], [-85.11432, 31.983041], [-85.11432, 31.98323], [-85.114295, 31.984865], [-85.11431, 31.98559], [-85.11433, 31.98596], [-85.11435, 31.98632], [-85.11442, 31.987049], [-85.11447, 31.987598], [-85.11452, 31.988143], [-85.1147, 31.989963], [-85.11474, 31.990402], [-85.11501, 31.99323], [-85.11552, 31.998318], [-85.11573, 32.000543], [-85.11577, 32.000934], [-85.11583, 32.001567], [-85.116, 32.003414], [-85.11606, 32.003993], [-85.11616, 32.004967], [-85.11621, 32.00559], [-85.11636, 32.00705], [-85.11661, 32.00972], [-85.11677, 32.011414], [-85.11691, 32.01287], [-85.11705, 32.014324], [-85.11719, 32.015779], [-85.11733, 32.017234], [-85.11737, 32.017595], [-85.11741, 32.017958], [-85.11747, 32.018321], [-85.11753, 32.01868], [-85.11761, 32.01904], [-85.11769, 32.0194], [-85.11779, 32.01975], [-85.11789, 32.020108], [-85.11856, 32.02222], [-85.11953, 32.02521], [-85.119704, 32.02573], [-85.11981, 32.02609], [-85.11993, 32.026437], [-85.11998, 32.02661], [-85.1201, 32.026986], [-85.12032, 32.02759], [-85.12046, 32.02793], [-85.1206, 32.028275], [-85.12091, 32.02895], [-85.12101, 32.029125], [-85.1213, 32.02968], [-85.12136, 32.02978], [-85.12145, 32.029935], [-85.12296, 32.032553], [-85.12349, 32.03347], [-85.12372, 32.03386], [-85.12435, 32.03496], [-85.12515, 32.03635], [-85.12531, 32.03662], [-85.12625, 32.03825], [-85.12633, 32.038393], [-85.12643, 32.038604], [-85.12662, 32.038933], [-85.12871, 32.042543], [-85.13002, 32.044804], [-85.13117, 32.046796], [-85.13249, 32.049087], [-85.13704, 32.056955], [-85.13816, 32.05893], [-85.13843, 32.05937], [-85.14007, 32.06218]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800670', 'FULLNAME': 'N Orange Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14203, 31.892963], [-85.14202, 31.89317], [-85.14204, 31.894324], [-85.14203, 31.89447], [-85.14211, 31.89459]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801264', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.27605, 31.70433], [-85.27601, 31.704426], [-85.27583, 31.704919], [-85.27562, 31.70554], [-85.27555, 31.70568], [-85.27492, 31.70742], [-85.27475, 31.707913], [-85.2747, 31.70802], [-85.27465, 31.70819], [-85.27437, 31.70895], [-85.27435, 31.70902], [-85.27427, 31.709223], [-85.27341, 31.711597], [-85.27333, 31.71181], [-85.27317, 31.712295], [-85.27289, 31.71306], [-85.27253, 31.71396], [-85.27213, 31.7148], [-85.27208, 31.71491], [-85.27201, 31.715039], [-85.27199, 31.715108], [-85.27192, 31.715232], [-85.27184, 31.71536], [-85.2718, 31.71542], [-85.27176, 31.71549], [-85.27171, 31.715558], [-85.27159, 31.715753], [-85.27109, 31.716514], [-85.27055, 31.717254], [-85.27005, 31.717852], [-85.26941, 31.71854], [-85.26856, 31.71936], [-85.26832, 31.719572], [-85.26783, 31.719977], [-85.26765, 31.72012], [-85.2676, 31.720174], [-85.26751, 31.72023], [-85.26681, 31.72079], [-85.26665, 31.720919], [-85.26658, 31.72097], [-85.26579, 31.721606], [-85.26453, 31.722613], [-85.264417, 31.722722], [-85.26405, 31.72302], [-85.26393, 31.72311], [-85.26384, 31.723169], [-85.26309, 31.723772], [-85.263, 31.723856], [-85.26281, 31.724001], [-85.262402, 31.72434], [-85.26231, 31.724399], [-85.25924, 31.726854], [-85.25888, 31.727146], [-85.25777, 31.72805], [-85.25695, 31.728794], [-85.25639, 31.729418], [-85.25624, 31.729596], [-85.25596, 31.729954], [-85.25468, 31.73186], [-85.25393, 31.733012], [-85.25216, 31.735679], [-85.25163, 31.736432], [-85.25141, 31.7367], [-85.25083, 31.737371], [-85.2504, 31.737812], [-85.25016, 31.738023], [-85.2498, 31.73833], [-85.24961, 31.73848], [-85.24871, 31.7391], [-85.24741, 31.739884], [-85.24508, 31.74127], [-85.24486, 31.74139], [-85.24467, 31.741505], [-85.24458, 31.741561], [-85.24362, 31.74213], [-85.24356, 31.74218], [-85.243445, 31.74224], [-85.24315, 31.742427], [-85.24301, 31.742507], [-85.24295, 31.742548], [-85.2429, 31.742574], [-85.24279, 31.74263], [-85.24075, 31.74384], [-85.23968, 31.74444], [-85.23939, 31.7446], [-85.23777, 31.74542], [-85.2365, 31.74599], [-85.23642, 31.74602], [-85.23484, 31.74674], [-85.23464, 31.74684], [-85.23454, 31.746874], [-85.2327, 31.7477], [-85.2325, 31.74779], [-85.23114, 31.748398], [-85.231, 31.74848], [-85.23026, 31.748809], [-85.23015, 31.74884], [-85.22826, 31.749693], [-85.22819, 31.749732], [-85.2275, 31.750039], [-85.22742, 31.75007], [-85.22719, 31.750173], [-85.22687, 31.750309], [-85.2267, 31.750398], [-85.223306, 31.751923], [-85.22324, 31.751959], [-85.22279, 31.752164], [-85.22272, 31.7522], [-85.22255, 31.75226], [-85.22244, 31.75231], [-85.222167, 31.752431], [-85.2196, 31.753588], [-85.21855, 31.754054], [-85.21837, 31.754138], [-85.2175, 31.754532], [-85.21705, 31.754731], [-85.21637, 31.755044], [-85.21456, 31.755857], [-85.21433, 31.755964], [-85.21418, 31.75604], [-85.21381, 31.756206], [-85.21368, 31.756267], [-85.21358, 31.756296], [-85.21351, 31.756327], [-85.21275, 31.756669], [-85.2117, 31.757157], [-85.21118, 31.757408], [-85.21082, 31.7576], [-85.21042, 31.75779], [-85.21028, 31.757873], [-85.2102, 31.757912], [-85.20999, 31.75803], [-85.20681, 31.75972], [-85.20667, 31.75981], [-85.20566, 31.760347], [-85.20551, 31.76043], [-85.20536, 31.760491], [-85.20028, 31.7632], [-85.19947, 31.763633], [-85.19936, 31.763692], [-85.198859, 31.763971], [-85.19791, 31.764472], [-85.19774, 31.764557], [-85.19761, 31.764618], [-85.19551, 31.765736], [-85.19398, 31.766536], [-85.19384, 31.766616], [-85.19282, 31.767135], [-85.19268, 31.767198], [-85.192011, 31.767541], [-85.19194, 31.767583], [-85.19165, 31.76773], [-85.19076, 31.76816], [-85.19059, 31.768265], [-85.19044, 31.768344], [-85.18898, 31.769091], [-85.188251, 31.769475], [-85.18775, 31.769759], [-85.18613, 31.770825], [-85.18491, 31.77165]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801256', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.16316, 31.82653], [-85.16318, 31.82668], [-85.16322, 31.826823]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801267', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.16419, 31.83086], [-85.16465, 31.83279], [-85.1647, 31.83297], [-85.16511, 31.83468], [-85.16519, 31.83512], [-85.16525, 31.835335], [-85.16534, 31.83568], [-85.16543, 31.836075], [-85.165459, 31.836179], [-85.16558, 31.836673], [-85.16575, 31.83741], [-85.16578, 31.837567], [-85.16586, 31.837898], [-85.16587, 31.837968], [-85.16595, 31.83822], [-85.16627, 31.839578], [-85.16635, 31.839918], [-85.16656, 31.840836], [-85.16672, 31.84143], [-85.1668, 31.84171], [-85.16709, 31.842925], [-85.167121, 31.843067], [-85.16718, 31.843353], [-85.16731, 31.84415], [-85.16734, 31.844454], [-85.16735, 31.844587], [-85.16739, 31.84495], [-85.1674, 31.84517], [-85.16741, 31.845534], [-85.16742, 31.84582], [-85.16741, 31.845966], [-85.16741, 31.846229], [-85.16739, 31.846744], [-85.167352, 31.847182], [-85.16725, 31.84797], [-85.16702, 31.849758], [-85.16685, 31.851015], [-85.166834, 31.851153], [-85.16661, 31.85283], [-85.166579, 31.853054], [-85.16654, 31.85341], [-85.16653, 31.85346], [-85.1665, 31.85363], [-85.1665, 31.85371], [-85.16649, 31.853759], [-85.16639, 31.85451], [-85.16637, 31.854654], [-85.16635, 31.854872], [-85.16633, 31.85494], [-85.16632, 31.85506], [-85.16624, 31.85567], [-85.16624, 31.855806], [-85.16618, 31.85624], [-85.16614, 31.85644], [-85.16609, 31.856833], [-85.16603, 31.85716], [-85.16598, 31.857546], [-85.16593, 31.85799], [-85.16583, 31.858781], [-85.1658, 31.858924], [-85.16571, 31.859375], [-85.16562, 31.859722], [-85.16544, 31.860286], [-85.16531, 31.860598], [-85.16524, 31.860759], [-85.16508, 31.8611], [-85.164898, 31.861432], [-85.1647, 31.861756], [-85.16453, 31.86201], [-85.16422, 31.86245], [-85.16382, 31.862921], [-85.16308, 31.863723], [-85.16304, 31.86378], [-85.16292, 31.86389], [-85.16182, 31.865096], [-85.16108, 31.865902], [-85.16077, 31.866244], [-85.16024, 31.866814], [-85.16003, 31.867044], [-85.15993, 31.86716], [-85.15961, 31.867503], [-85.15945, 31.867677], [-85.15939, 31.867734], [-85.15912, 31.868016], [-85.15889, 31.868239], [-85.15873, 31.8684], [-85.1584, 31.86874], [-85.15819, 31.86897], [-85.15816, 31.86909]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685802088', 'FULLNAME': 'S Randolph Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.14956, 31.86662], [-85.14918, 31.867135], [-85.14896, 31.867445], [-85.14885, 31.86758], [-85.14859, 31.86795], [-85.14845, 31.86814], [-85.14805, 31.868694], [-85.14783, 31.869], [-85.1477, 31.86918], [-85.1476, 31.86934], [-85.14746, 31.86955], [-85.14708, 31.87013], [-85.14694, 31.87036], [-85.14662, 31.87088], [-85.14636, 31.871309], [-85.1456, 31.87261], [-85.14546, 31.87284], [-85.14493, 31.87373], [-85.14487, 31.873836], [-85.14467, 31.87419], [-85.14447, 31.874645], [-85.14446, 31.87466], [-85.14443, 31.87475], [-85.14436, 31.874976], [-85.144318, 31.875226], [-85.14427, 31.87574], [-85.14426, 31.87625], [-85.14426, 31.87637], [-85.14424, 31.87763], [-85.14418, 31.879397], [-85.14418, 31.88081], [-85.14416, 31.881752], [-85.14416, 31.88196], [-85.14416, 31.88219], [-85.14415, 31.882724], [-85.14414, 31.88322], [-85.14412, 31.883677], [-85.14413, 31.883894], [-85.14411, 31.884175], [-85.14411, 31.8844], [-85.14411, 31.88462], [-85.14409, 31.88485], [-85.144089, 31.88506], [-85.14407, 31.88558], [-85.14406, 31.88607], [-85.14405, 31.88637], [-85.14402, 31.886671], [-85.14402, 31.886792], [-85.144, 31.88705], [-85.143965, 31.887783], [-85.14395, 31.888292], [-85.14395, 31.88855], [-85.14393, 31.88935], [-85.14392, 31.89016], [-85.14392, 31.890266], [-85.14389, 31.890878]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801266', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.16438, 31.83083], [-85.16484, 31.832737], [-85.16487, 31.83292], [-85.16518, 31.834204], [-85.16524, 31.83441], [-85.16543, 31.835196], [-85.16544, 31.835277], [-85.16548, 31.835415], [-85.16555, 31.8357], [-85.16556, 31.835746], [-85.16559, 31.83584], [-85.16577, 31.83662], [-85.16581, 31.836769], [-85.16599, 31.83752], [-85.16601, 31.837639], [-85.16603, 31.837702], [-85.1661, 31.83799], [-85.16633, 31.83891], [-85.16652, 31.839698], [-85.16656, 31.839881], [-85.1667, 31.840484], [-85.16676, 31.840698], [-85.16676, 31.840779], [-85.16679, 31.840847], [-85.16681, 31.84093], [-85.1669, 31.84127], [-85.16691, 31.84137], [-85.166973, 31.84158], [-85.16702, 31.84178], [-85.167114, 31.842137], [-85.16715, 31.842295], [-85.16718, 31.842345], [-85.16726, 31.842702], [-85.16726, 31.842781], [-85.1673, 31.84293], [-85.16731, 31.843002], [-85.16733, 31.843066], [-85.16742, 31.84357], [-85.16753, 31.84423], [-85.16753, 31.844337], [-85.16755, 31.844441], [-85.16755, 31.844474], [-85.167581, 31.844817], [-85.16761, 31.845252], [-85.16762, 31.84576], [-85.16763, 31.84604], [-85.16763, 31.84628], [-85.16762, 31.84655], [-85.16757, 31.84706], [-85.16755, 31.847205], [-85.16746, 31.84797], [-85.16743, 31.84823], [-85.16724, 31.84967], [-85.16704, 31.85118], [-85.16682, 31.85283], [-85.16673, 31.8535], [-85.16671, 31.853676], [-85.16669, 31.85377], [-85.1665, 31.85525], [-85.16636, 31.856318], [-85.16634, 31.85646], [-85.16627, 31.85697], [-85.16625, 31.85715], [-85.16618, 31.857644], [-85.16614, 31.857985], [-85.16612, 31.858157], [-85.16609, 31.858447], [-85.16607, 31.858592], [-85.16602, 31.858938], [-85.1659, 31.859434], [-85.16584, 31.859645], [-85.16578, 31.859855], [-85.1657, 31.860137], [-85.16565, 31.860276], [-85.16559, 31.860409], [-85.16551, 31.8606], [-85.16544, 31.86076], [-85.16518, 31.86129], [-85.16491, 31.86175], [-85.16462, 31.86219], [-85.16429, 31.86261], [-85.16393, 31.863019], [-85.16357, 31.86342], [-85.1632, 31.86382], [-85.16309, 31.86394], [-85.16188, 31.865265], [-85.1612, 31.866002], [-85.15964, 31.867699], [-85.15929, 31.86809], [-85.15913, 31.86826], [-85.158958, 31.86842], [-85.15879, 31.868586], [-85.15862, 31.86875], [-85.15849, 31.86887], [-85.15827, 31.86905], [-85.15816, 31.86909]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801303', 'FULLNAME': 'S Forsyth Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.13868, 31.885637], [-85.1387, 31.885772], [-85.13872, 31.8859], [-85.13874, 31.886024], [-85.13873, 31.886279]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801283', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1400'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.1648, 31.799822], [-85.163703, 31.799604]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685800610', 'FULLNAME': 'S Midway St', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.44973, 31.87613], [-85.44971, 31.876317], [-85.4497, 31.876449]]}}, + {'type': 'Feature', 'properties': {'LINEARID': '110685801265', 'FULLNAME': 'S Eufaula Ave', 'RTTYP': 'M', 'MTFCC': 'S1200'}, 'geometry': {'type': 'LineString', 'coordinates': [[-85.15816, 31.86909], [-85.15806, 31.86918], [-85.15796, 31.86931], [-85.15783, 31.86945], [-85.15752, 31.86977], [-85.15701, 31.870255], [-85.1564, 31.87087], [-85.15622, 31.87103], [-85.15601, 31.87123], [-85.15534, 31.871922], [-85.15468, 31.87258], [-85.15391, 31.87336], [-85.1534, 31.8739], [-85.15316, 31.874155], [-85.15299, 31.874324], [-85.15237, 31.87494], [-85.15199, 31.87533], [-85.15105, 31.87628], [-85.1508, 31.87653], [-85.15056, 31.87677], [-85.15028, 31.87706], [-85.15018, 31.87718], [-85.14964, 31.87771], [-85.1494, 31.87792], [-85.14873, 31.87859], [-85.14824, 31.87908], [-85.14779, 31.879528], [-85.14747, 31.87986], [-85.14732, 31.88004], [-85.14679, 31.88074]]}} + ]; + + var myMap; + + beforeEach(function () { + imageTest.prepareImageTest(); + }); + + afterEach(function () { + myMap.exit(); + }); + + it('lines', function (done) { + + var mapOptions = {center: {y: 31.87798, x: -85.44956}, zoom: 10}; + myMap = common.createOsmMap(mapOptions, {}, true); + + var layer = myMap.createLayer('feature'); + var style = { + 'strokeColor': {r: 1, g: 0.2, b: 0}, + 'strokeWidth': 2.0 + }; + + layer.createFeature('line') + .data(data) + .line(function (d) { return d.geometry.coordinates; }) + .position(function (d, index, d2, index2) { + return {x: d[0], y: d[1]}; + }) + .style(style); + myMap.draw(); + + imageTest.imageTest('glLines', 0.0015, done, myMap.onIdle, 0, 2); + }); +}); diff --git a/tests/image-test.js b/tests/image-test.js new file mode 100644 index 0000000000..faf582e51f --- /dev/null +++ b/tests/image-test.js @@ -0,0 +1,111 @@ +// var resemble = require('node-resemble'); +var $ = require('jquery'); + +module.exports = {}; + +/* Compare an image from a canvas object by sending it to our express server + * letting it do the work. + * + * @param {string} name: name of the base image. This is probably the name of + * the test. + * @param {HTMLCanvasObject} canvas: the image is obtained via + * canvas.toDataURL(). + * @param {number} threshold: allowed difference between this image and the + * base image. + * @param {function} callback: a function to call when complete. + * @returns {object} jquery ajax promise. + */ +function compareImage(name, canvas, threshold, callback) { + if (threshold === undefined) { + threshold = 0.001; + } + return $.ajax({ + url: '/testImage?compare=true&threshold=' + encodeURIComponent(threshold) + '&name=' + encodeURIComponent(name), + data: '' + canvas.toDataURL(), + method: 'PUT', + contentType: 'image/png', + dataType: 'json' + }).done(function (data) { + expect(Number(data.misMatchPercentage) * 0.01).not.toBeGreaterThan(threshold); + if (callback) { + callback(); + } + }); +} + +/** + * Call this before creating any geojs canvas instances to ensure that the + * image test can collect data. + */ +module.exports.prepareImageTest = function () { + window.contextPreserveDrawingBuffer = true; + $('#map').remove(); + var map = $('
').css({width: '800px', height: '600px'}); + $('body').css({overflow: 'hidden'}).append(map); +}; + +/** + * Compare a composite of all canvas elements with a base image. + * + * @param {string} name: name of the base image. This is probably the name of + * the test. + * @param {number} threshold: allowed difference between this image and the + * base image. + * @param {function} doneFunc: a function to call when complete. Optional. + * @param {function} idleFunc: a function to call to ensure that the map is + * idle. No other tests or delays are supplied until this is called. It + * is expected to take a callback function itself. + * @param {number} delay: additional delay in milliseconds to wait after idle. + * @param {integer} rafCount: additional number of renderAnimationFrames to + * wait after the delay. + */ +module.exports.imageTest = function (name, threshold, doneFunc, idleFunc, delay, rafCount) { + var deferred = $.Deferred(); + + var readyFunc = function () { + var result = $('')[0]; + result.width = $('canvas')[0].width; + result.height = $('canvas')[0].height; + var context = result.getContext('2d'); + context.fillStyle = 'white'; + context.fillRect(0, 0, result.width, result.height); + $('canvas').each(function () { + context.drawImage($(this)[0], 0, 0); + }); + compareImage(name, result, threshold, function () { + if (doneFunc) { + doneFunc(); + } + deferred.resolve(); + }); + }; + + var rafCounter = rafCount === undefined ? 2 : rafCount; + + if (rafCounter) { + var rafCallback = readyFunc; + var rafFunc = function () { + if (rafCounter <= 0) { + rafCallback(); + } else { + window.requestAnimationFrame(function () { + rafCounter -= 1; + rafFunc(); + }); + } + }; + readyFunc = rafFunc; + } + + if (delay) { + var delayFunc = readyFunc; + readyFunc = window.setTimeout(delayFunc, delay); + } + + if (idleFunc) { + idleFunc(readyFunc); + } else { + readyFunc(); + } + return deferred; +}; diff --git a/tests/test-common.js b/tests/test-common.js new file mode 100644 index 0000000000..28ace16c67 --- /dev/null +++ b/tests/test-common.js @@ -0,0 +1,112 @@ +// General utilities that are made available to tests. + +var $ = require('jquery'); +var geo = require('../src'); + +module.exports = { + createOsmMap: function (mapOpts, osmOpts, notiles) { + // Generate a default open street maps layer with optional + // arguments. If no options present, creates the map + // in $('#map') with tiles served from the local server. + + 'use strict'; + + mapOpts = mapOpts || {}; + osmOpts = osmOpts || {}; + notiles = !!notiles; + + var mapDefaults = { + node: '#map', + zoom: 2.5, + center: { + x: 0, + y: 0 + }, + wrapX: false, + clampBoundsX: true + }; + $.extend(true, mapDefaults, mapOpts); + + var osmDefaults = { + baseUrl: '/data/tiles/', + attribution: null + }; + + if (notiles) { + osmDefaults.url = function () { + return '/data/white.jpg'; + }; + delete osmDefaults.baseUrl; + } + $.extend(true, osmDefaults, osmOpts); + + if (osmDefaults.url) { + delete osmDefaults.baseUrl; + } + + var map = geo.map(mapDefaults); + if (!notiles) { + map.createLayer('osm', osmDefaults); + } + + map.interactor().options({ + momentum: false, + throttle: 0 + }); + + if (osmDefaults.m_baseUrl) { + // change the zoom range to represent the + // tiles present in the local dataset + map.zoomRange({ + min: 0, + max: 3 + }); + } + + return map; + }, + + loadCitiesData: function (done, n) { + 'use strict'; + + // Load at most n rows of the cities dataset. + $.ajax({ + type: 'GET', + url: '/data/cities.csv', + dataType: 'text', + success: function (data) { + function processCSVData(csvdata) { + var table = []; + var lines = csvdata.split(/\r\n|\n/); + + lines.forEach(function (line, i) { + if (n && i >= n) { + return; + } + if (line.length) { + table.push(line.split(',')); + } + }); + return table; + } + + var table = processCSVData(data); + var citieslatlon = []; + var i; + for (i = 0; i < table.length; i += 1) { + if (table[i][2] !== undefined) { + var lat = table[i][2]; + lat = lat.replace(/(^\s+|\s+$|^"|"$)/g, ''); + lat = parseFloat(lat); + + var lon = table[i][3]; + lon = lon.replace(/(^\s+|\s+$|^"|"$)/g, ''); + lon = parseFloat(lon); + citieslatlon.push({lon: lon, lat: lat, elev: 0.0}); + } + } + done(citieslatlon); + } + }); + } +}; diff --git a/tests/test-gl.js b/tests/test-gl.js new file mode 100644 index 0000000000..cb9c830d6a --- /dev/null +++ b/tests/test-gl.js @@ -0,0 +1,9 @@ +/** + * Entry point for all tests in ./gl-cases/* + * This is here to prevent webpack from creating a seperate bundle for each + * test case. See: https://github.com/webpack/karma-webpack/issues/23 + */ + +var tests = require.context('./gl-cases', true, /.*\.js$/); +tests.keys().forEach(tests); + diff --git a/tests/test-unit.js b/tests/test-unit.js new file mode 100644 index 0000000000..25257e9099 --- /dev/null +++ b/tests/test-unit.js @@ -0,0 +1,7 @@ +/** + * Entry point for all tests in ./cases/* + * This is here to prevent webpack from create a seperate bundle for each + * test case. See: https://github.com/webpack/karma-webpack/issues/23 + */ +var tests = require.context('./cases', true, /.*\.js$/); +tests.keys().forEach(tests); diff --git a/tests/test-utils.js b/tests/test-utils.js index 49aecdcb35..1bc68c5bd8 100644 --- a/tests/test-utils.js +++ b/tests/test-utils.js @@ -263,8 +263,12 @@ module.exports.logCanvas2D = function logCanvas2D(enable) { var log = {enable: enable, counts: {}, log: []}; var proto = CanvasRenderingContext2D.prototype; - $.each(proto, function (key) { - var orig = proto[key]; + $.each(Object.keys(proto), function (idx, key) { + try { + var orig = proto[key]; + } catch (err) { + return; + } if (orig && orig.constructor && orig.call && orig.apply) { proto[key] = function () { log.counts[key] = (log.counts[key] || 0) + 1;