diff --git a/.github/config.yml b/.github/config.yml index 91a42fbbf..33ce51279 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -20,6 +20,14 @@ firstPRMergeComment: | Your code will likely be published to https://mapknitter.org in the next few days. In the meantime, can you tell us your Twitter handle so we can thank you properly? Now that you've completed this, you can help someone else take their first step! - See: [Public Lab's coding community!](https://code.publiclab.org) + Reach out to someone else working on theirs on [Public Lab's code welcome page](https://code.publiclab.org#r=all). Thanks! +
+ Help others take their first step +

Now that you've merged your first pull request, you're the perfect person to help someone else out with this challenging first step. 🙌

+

https://code.publiclab.org

+

Try looking at this list of `first-timers-only` issues, and see if someone else is waiting for feedback, or even stuck! 😕

+

People often get stuck at the same steps, so you might be able to help someone get unstuck, or help lead them to some documentation that'd help. Reach out and be encouraging and friendly! 😄 🎉

+

Read about how to help support another newcomer here, or find other ways to offer mutual support here.

+
# It is recommended to include as many gifs and emojis as possible diff --git a/.github/first-timers-issue-template.md b/.github/first-timers-issue-template.md index 950f0cc38..f013fbcfa 100644 --- a/.github/first-timers-issue-template.md +++ b/.github/first-timers-issue-template.md @@ -1,10 +1,10 @@ -Hi, this is a [first-timers-only issue](https://code.publiclab.org#r=all). This means we've worked to make it more legible to folks who either **haven't contributed to our codebase before, or even folks who haven't contributed to open source before**. +Hi, this is a [first-timers-only issue](https://code.publiclab.org/#r=all). This means we've worked to make it more legible to folks who either **haven't contributed to our codebase before, or even folks who haven't contributed to open source before**. If that's you, we're interested in helping you take the first step and can answer questions and help you out as you do. Note that we're especially interested in contributions from people from groups underrepresented in free and open source software! We know that the process of creating a pull request is the biggest barrier for new contributors. This issue is for you 💝 -If you have contributed before, **consider leaving this one for someone new**, and looking through our general [help wanted](https://github.com/publiclab/mapknitter/labels/help-wanted) issues. Thanks! +If you have contributed before, **consider leaving this one for someone new**, and looking through our general [help wanted](https://github.com/publiclab/plots2/labels/help-wanted) issues. Thanks! ### 🤔 What you will need to know. @@ -16,6 +16,8 @@ Nothing. This issue is meant to welcome you to Open Source :) We are happy to wa - [ ] 📝 **Update** the file [$FILENAME]($BRANCH_URL) in the `$REPO` repository (press the little pen Icon) and edit the line as shown below. +To get help installing the application on your computer, see [the project README](https://github.com/publiclab/mapknitter/tree/main/README.md) and get help at #307 + [See this page](https://code.publiclab.org/#r=all) for some help in taking your first steps! Below is a "diff" showing in red (and a `-`) which lines to remove, and in green (and a `+`) which lines to add: @@ -42,6 +44,6 @@ Leave a comment below! ### Is someone else already working on this? -We encourage you to link to this issue by mentioning the issue # in your pull request, so we can see if someone's already started on it. **If someone seem stuck, offer them some help!** Otherwise, [take a look at some other issues you can help with](https://publiclab.github.io/community-toolbox/#r=all). Thanks! +We encourage you to link to this issue by mentioning the issue # in your pull request, so we can see if someone's already started on it. **If someone seem stuck, offer them some help!** Otherwise, [take a look at some other issues you can help with](https://code.publiclab.org/#r=all). Thanks! (This issue was created by [First-Timers-Bot](https://github.com/hoodiehq/first-timers-bot).) diff --git a/.github/first-timers.yml b/.github/first-timers.yml new file mode 100644 index 000000000..78367ef38 --- /dev/null +++ b/.github/first-timers.yml @@ -0,0 +1,12 @@ +# You can change the labels to suit your needs if "first-timers-only" is not what you are looking for. +# These are some examples. +labels: + - first-timers-only + - help wanted + +#If you would like to add your own template for the issue, add an .md file to your .github folder +template: .github/first-timers-issue-template.md + +# You can create the issue in a different repo than where the problem is. Just make sure you installed the bot on the configured repository. +# The issue will link back to the original repository where the contribution will be made. +#repository: repo-name diff --git a/.travis.yml b/.travis.yml index 8444c0721..dacd3f3d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,10 @@ install: - cp config/database.yml.example config/database.yml - cp config/config.yml.example config/config.yml - cp db/schema.rb.example db/schema.rb - - docker-compose build - - docker-compose run web sleep 10 - - docker-compose run web bash -c "rake db:setup" - - docker-compose run web bash -c "rake db:migrate" - - docker-compose run web bower install --allow-root + - docker-compose up -d --build + - docker-compose exec web bash -l -c "sleep 10" + - docker-compose exec web bash -l -c "bundle install" + - docker-compose exec web bash -l -c "rake db:setup || rake db:migrate" script: - - docker-compose run web bash -c "rake test" + - docker-compose exec web bash -l -c "rake test" diff --git a/Dockerfile b/Dockerfile index 6f1a4153d..5fbfd32b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,46 @@ # Dockerfile # Mapknitter # https://github.com/publiclab/mapknitter/ +# This image deploys Mapknitter! -FROM ruby:2.4.4-stretch -MAINTAINER Sebastian Silva "sebastian@fuentelibre.org" - -LABEL This image deploys Mapknitter! +FROM debian:buster # Set correct environment variables. -RUN mkdir -p /app ENV HOME /root # Install dependencies -RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - -RUN apt-get update -qq && apt-get install -y bundler default-libmysqlclient-dev ruby-rmagick libfreeimage3 libfreeimage-dev ruby-dev gdal-bin python-gdal curl libcurl4-openssl-dev libssl-dev zip nodejs ##ALSO TRIED: ruby-pg +RUN apt-get update -qq && apt-get install -y \ + bundler ruby-rmagick libfreeimage3 \ + libfreeimage-dev zip nodejs gdal-bin \ + curl g++ gcc autoconf automake bison \ + libc6-dev libffi-dev libgdbm-dev \ + libncurses5-dev libsqlite3-dev libtool \ + libyaml-dev make pkg-config sqlite3 \ + zlib1g-dev libgmp-dev libreadline-dev libssl-dev \ + procps libmariadb-dev-compat libmariadb-dev git python-gdal \ + imagemagick + +# Ruby +RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB && curl -sSL https://get.rvm.io | bash -s stable && usermod -a -G rvm root +RUN /bin/bash -l -c ". /etc/profile.d/rvm.sh && rvm install 2.4.4 && rvm use 2.4.4 --default" + +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt-get install -y npm RUN npm install -g bower + # Install bundle of gems +SHELL [ "/bin/bash", "-l", "-c" ] WORKDIR /tmp ADD Gemfile /tmp/Gemfile ADD Gemfile.lock /tmp/Gemfile.lock RUN bundle install +# HOTFIX Workaround for mysql2 gem incompatibility with libmariadb-dev +RUN sed -i "s/ LONG_PASSWORD |//g" /usr/local/rvm/gems/ruby-*/gems/mysql2-*/lib/mysql2/client.rb + # Add the Rails app WORKDIR /app -ADD . /app -RUN bower install --allow-root +COPY Gemfile /app/Gemfile +COPY Gemfile.lock /app/Gemfile.lock +COPY start.sh /app/start.sh + +CMD [ "bash", "-l", "start.sh" ] diff --git a/Gemfile b/Gemfile index 9c5d82a88..5d7695a30 100644 --- a/Gemfile +++ b/Gemfile @@ -4,13 +4,13 @@ ruby "2.4.4" gem "rails", "~>3.2" gem 'rake', '~> 12.3.2' -gem "will_paginate", "3.1.6" +gem "will_paginate", "3.1.7" gem "will_paginate-bootstrap" gem "friendly_id" # dependencies group :dependencies do - gem 'mysql2', '~> 0.3.20' + gem 'mysql2', '< 0.4' gem "geokit-rails", "1.1.4" gem "image_science", "1.2.6" gem "recaptcha", :require => "recaptcha/rails" @@ -31,7 +31,7 @@ group :dependencies do # asset pipelining gem "sprockets"#, "2.12.1" - gem "sass" + gem "sass", :require => 'sass' gem "autoprefixer-rails" gem "uglifier" diff --git a/Gemfile.lock b/Gemfile.lock index 8c16dde90..e41685fa7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,7 +32,7 @@ GEM i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) arel (3.0.3) - autoprefixer-rails (9.4.7) + autoprefixer-rails (9.4.8) execjs aws-sdk (1.5.8) httparty (~> 0.7) @@ -159,8 +159,8 @@ GEM uglifier (4.1.20) execjs (>= 0.3.0, < 3) uuidtools (2.1.5) - will_paginate (3.1.6) - will_paginate-bootstrap (1.0.1) + will_paginate (3.1.7) + will_paginate-bootstrap (1.0.2) will_paginate (>= 3.0.3) PLATFORMS @@ -174,7 +174,7 @@ DEPENDENCIES geokit-rails (= 1.1.4) image_science (= 1.2.6) jshintrb - mysql2 (~> 0.3.20) + mysql2 (< 0.4) oa-openid (= 0.3.2) open_id_authentication paperclip (~> 4.2.2) @@ -191,11 +191,11 @@ DEPENDENCIES test-unit therubyracer uglifier - will_paginate (= 3.1.6) + will_paginate (= 3.1.7) will_paginate-bootstrap RUBY VERSION ruby 2.4.4p296 BUNDLED WITH - 1.16.2 + 1.17.1 diff --git a/MYSQL.md b/MYSQL.md new file mode 100644 index 000000000..711b3f960 --- /dev/null +++ b/MYSQL.md @@ -0,0 +1,149 @@ +# installation troubleshooting & instructions + +## System Agnostic + +- bundler skipping over **mysql2** gem? + +```Bash + +$ rm .bundle/config + +$ bundle exec bundle install + +``` + + + +## MacOS + +**Homebrew setup:** + +(Note: alternative to Homebrew is [mySQL community server](https://dev.mysql.com/downloads/mysql/5.7.html#downloads) - available for all systems) + +Dependencies: + +- `cmake` + +- `openssl` + +```Bash + +$ brew install cmake + +$ brew install openssl + +``` + +Installation: + +```Bash + +#make sure you don't have any other versions of mysql installed +$ brew list + +#if you do +$ brew uninstall +$ brew unlink + +#install 5.7 +$ brew install mysql@5.7 + +$ brew link mysql@5.7 --force +``` + +Test Usage: + +```Bash + +# install brew services +$ brew tap homebrew/services + +# cmd to run always - suggest aliasing this in your bash profile +$ brew services start mysql@5.7 + +#confirm its running +$ brew services list + +# cmd to stop running +$ brew services stop mysql@5.7 + +``` + +Update Permissions + +```Bash +# check for right permissions to the PIDs +$ ls -laF /usr/local/var/mysql/ + +# if the owner is root you should change it to mysql or username +$ sudo chown -R /usr/local/var/mysql/ + +# confirm updated permissions +$ ls -laF /usr/local/var/mysql/ + +``` + +Account Setup + +```Bash +# secure your account +$ mysql_secure_installation + +# set password +$ mysqladmin -u root password +# login -- not root anymore +$ mysql -u -p + +``` + +Permission issues above? + +(note these commands also fix the error: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)) + +```Bash + +$ mysql.server stop + +#unset the temporary directory +$ echo $TMPDIR +$ unset TMPDIR +$ echo $TMPDIR + +$ whoami + +$ mysqld -initialize --verbose --user=$(whoami) --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp + +#restart mysql +$ mysql.server restart + + +$ mysql -u root + +#You should now be in the mysql command line shell +mysql> SELECT User, Host, authentication_string FROM mysql.user; + +mysql> rename user 'root'@'localhost' to ''@'localhost'; + +#confirm +mysql> SELECT User, Host, authentication_string FROM mysql.user; + +mysql> flush privileges; + +mysql> exit + +``` + +Reconfirm Access + +(whenever want to access the mysql db locally, need to run this login first - suggest aliasing in bash profile) + +```Bash + +$ mysql -u -p + +``` + + + +## Pending: please add instructions for your respective system + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..d051bfc37 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +export COMPOSE_HTTP_TIMEOUT=360 + +build: + cp config/database.yml.example config/database.yml + cp db/schema.rb.example db/schema.rb + docker-compose build + docker-compose run web bash -l -c "sleep 10 && rake db:setup && rake db:migrate && rake assets:precompile" + +deploy-container: + docker-compose up -d + +redeploy-container: + docker-compose up --force-create -d diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 9e326e5bd..0198120c9 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,5 @@ +References \#0000 (\<=== Add issue number here) + Make sure these boxes are checked before your pull request is ready to be reviewed and merged. Thanks! * [ ] tests pass -- `rake test` diff --git a/README.md b/README.md index b6da44829..ff2a8c287 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,9 @@ Install rvm for Ruby management (http://rvm.io) **Ubuntu users:** You may need to enable `Run command as a login shell` in Ubuntu's Terminal, under Profile Preferences > Title and Command. Then close the terminal and reopen it. -Then, use RVM to install version 2.1.2 of Ruby: +Then, use RVM to install version 2.4.4 of Ruby: -`rvm install 2.1.2` +`rvm install 2.4.4` You'll also need **bower** which is available through NPM. To install NPM, you can run: @@ -88,14 +88,14 @@ Once NPM is installed, you should be able to run: ## Installation -You'll need at least Ruby v1.9.3 (**v2.1.x** preferred) +You'll need Ruby v2.4.4 (use your local ruby version management system - RVM / rbenv / etc. - to install and set locally) 1. Download a copy of the source with `git clone https://github.com/publiclab/mapknitter.git` 2. Install gems with `bundle install` from the rails root folder. You may need to run `bundle update` if you have older gems in your environment. 3. Copy and configure config/database.yml from config/database.yml.example, using a new empty database you've created 4. Copy and configure config/config.yml from config/config.yml.example (for now, this is only for the [Google Maps API Key, which is optional](http://stackoverflow.com/questions/2769148/whats-the-api-key-for-in-google-maps-api-v3)) 5. Initialize database with `bundle exec rake db:setup` -6. Enter ReCaptcha public and private keys in config/initializers/recaptcha.rb, copied from recaptcha.rb.example. To get keys, visit https://google.com/recaptcha/admin +6. Enter ReCaptcha public and private keys in config/initializers/recaptcha.rb, copied from recaptcha.rb.example. To get keys, visit https://www.google.com/recaptcha/admin/create 7. Install static assets (like external javascript libraries, fonts) with `bower install` 8. Start rails with `bundle exec passenger start` from the Rails root and open http://localhost:3000 in a web browser. (For some, just `passenger start` will work; adding `bundle exec` ensures you're using the version of passenger you just installed with Bundler.) diff --git a/Rakefile b/Rakefile index 8708a7df0..b6446b5e1 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,6 @@ #!/usr/bin/env rake # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - require File.expand_path('../config/application', __FILE__) -Mapknitter::Application.load_tasks +Mapknitter::Application.load_tasks \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 79057b5d1..068faeff6 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -20,8 +20,6 @@ //= require leaflet-providers/leaflet-providers.js //= require leaflet-toolbar/dist/leaflet.toolbar.js //= require leaflet-distortableimage/dist/leaflet.distortableimage.js -//= require leaflet-easybutton/src/easy-button.js -//= require leaflet-google/index.js //= require sparklines/source/sparkline.js //= require annotations-legacy.js //= require glfx-js/dist/glfx.js diff --git a/app/assets/javascripts/maps.js b/app/assets/javascripts/maps.js index 291f25edc..90a586db6 100644 --- a/app/assets/javascripts/maps.js +++ b/app/assets/javascripts/maps.js @@ -2,7 +2,6 @@ //= require knitter //= require exif-js/exif.js //= require mapknitter -//= require seiyria-bootstrap-slider/dist/bootstrap-slider.min.js /* Move navbar links into dropdown if nav is inside the sidebar. */ jQuery(document).ready(function($) { diff --git a/app/assets/stylesheets/header.css.scss b/app/assets/stylesheets/header.css.scss index 131e95589..49e9631a6 100644 --- a/app/assets/stylesheets/header.css.scss +++ b/app/assets/stylesheets/header.css.scss @@ -1 +1,50 @@ -body { padding-top: 65px; } \ No newline at end of file +body { + padding-top: 60px; +} + +/* override Bootstrap 3 navbar collapse for medium screen sizes */ +@media (max-width: 1200px) { + .navbar-header { + float: none; + } + + .navbar-left,.navbar-right { + float: none !important; + } + + .navbar-toggle { + display: block; + } + + .navbar-collapse { + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); + } + + .navbar-fixed-top { + top: 0; + border-width: 0 0 1px; + } + + .navbar-collapse.collapse { + display: none !important; + } + + .navbar-nav { + float: none !important; + margin-top: 7.5px; + } + + .navbar-nav > li { + float: none; + } + + .navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + } + + .collapse.in{ + display:block !important; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/maps.css.scss b/app/assets/stylesheets/maps.css.scss index d130286fa..895175dc5 100644 --- a/app/assets/stylesheets/maps.css.scss +++ b/app/assets/stylesheets/maps.css.scss @@ -1,5 +1,4 @@ /* - *= require seiyria-bootstrap-slider/dist/css/bootstrap-slider.min.css *= require jquery-ui/themes/overcast/jquery-ui.min.css */ /* Variables */ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d81450d1f..2d593551f 100755 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,7 +6,7 @@ class ApplicationController < ActionController::Base helper :all # include all helpers, all the time - before_filter :current_user, :check_subdomain + before_filter :current_user helper_method :logged_in? def current_user @@ -22,19 +22,13 @@ def current_user end end - def check_subdomain - if request.subdomain.present? && Rails.env != 'test' - redirect_to 'http://' + request.domain + request.port_string + request.fullpath - end - end - private def require_login unless logged_in? path_info = request.env['PATH_INFO'] flash[:warning] = "You must be logged in to access this section" - redirect_to '/login?back_to=' + URI.encode(path_info) # halts request cycle + redirect_to '/login?back_to=' + path_info.to_param # halts request cycle end end diff --git a/app/controllers/utility_controller.rb b/app/controllers/utility_controller.rb index 7d606b874..21ca7aaf6 100644 --- a/app/controllers/utility_controller.rb +++ b/app/controllers/utility_controller.rb @@ -9,7 +9,4 @@ def tms_alt redirect_to "/tms/#{params[:id]}/#{params[:z]}/#{params[:x]}/#{y}.png" end - def tms_info - end - end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c4790d45d..2122d43c4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -47,6 +47,6 @@ def csrf_meta_tags out % [ Rack::Utils.escape_html(request_forgery_protection_token), Rack::Utils.escape_html(form_authenticity_token) ] end - end + end end diff --git a/app/models/map.rb b/app/models/map.rb index 398a0dff3..fabfaea1c 100755 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -163,8 +163,8 @@ def average_cm_per_pixel res = 1 if res == 0 # let's not ever try to go for infinite resolution scales << res unless res == nil end - sum = (scales.inject {|sum, n| sum + n }) if scales - average = sum/count if sum + total_sum = (scales.inject {|sum, n| sum + n }) if scales + average = total_sum/count if total_sum average else 0 @@ -202,164 +202,23 @@ def grouped_images_histogram(binsize) hist end - def run_export(user,resolution) - begin - unless export = self.export - export = Export.new({ - :map_id => self.id - }) - end - export.user_id = user.id if user - export.status = 'starting' - export.tms = false - export.geotiff = false - export.zip = false - export.jpg = false - export.save - - directory = "#{Rails.root}/public/warps/"+self.slug+"/" - stdin, stdout, stderr = Open3.popen3('rm -r '+directory.to_s) - puts stdout.readlines - puts stderr.readlines - stdin, stdout, stderr = Open3.popen3("rm -r #{Rails.root}/public/tms/#{self.slug}") - puts stdout.readlines - puts stderr.readlines - - puts '> averaging scales' - pxperm = 100/(resolution).to_f || self.average_scale # pixels per meter - - puts '> distorting warpables' - - origin = self.distort_warpables(pxperm, self.placed_warpables, self.latest_export) - warpable_coords = origin.pop - - export = self.export - export.status = 'compositing' - export.save - - puts '> generating composite tiff' - composite_location = self.generate_composite_tiff(warpable_coords,origin) - - info = (`identify -quiet -format '%b,%w,%h' #{composite_location}`).split(',') - puts info - - export = self.export - if info[0] != '' - export.geotiff = true - export.size = info[0] - export.width = info[1] - export.height = info[2] - export.cm_per_pixel = 100.0000/pxperm - export.status = 'tiling' - export.save - end - - puts '> generating tiles' - export = self.export - export.tms = true if self.generate_tiles - export.status = 'zipping tiles' - export.save - - puts '> zipping tiles' - export = self.export - export.zip = true if self.zip_tiles - export.status = 'creating jpg' - export.save - - puts '> generating jpg' - export = self.export - export.jpg = true if self.generate_jpg - export.status = 'complete' - export.save - - rescue SystemCallError - export = self.export - export.status = 'failed' - export.save - end - return export.status - end - - # distort all warpables, returns upper left corner coords in x,y - def distort_warpables(scale, warpables, export) - - puts '> generating geotiffs of each warpable in GDAL' - lowest_x=0 - lowest_y=0 - warpable_coords = [] - current = 0 - warpables.each do |warpable| - current += 1 - - export.status = 'warping '+current.to_s+' of '+warpables.length.to_s - puts 'warping '+current.to_s+' of '+warpables.length.to_s - export.save - - my_warpable_coords = warpable.generate_perspectival_distort(scale,self.slug) - puts '- '+my_warpable_coords.to_s - warpable_coords << my_warpable_coords - lowest_x = my_warpable_coords.first if (my_warpable_coords.first < lowest_x || lowest_x == 0) - lowest_y = my_warpable_coords.last if (my_warpable_coords.last < lowest_y || lowest_y == 0) - end - [lowest_x,lowest_y,warpable_coords] - end - - # generate a tiff from all warpable images in this set - def generate_composite_tiff(coords,origin) - directory = "public/warps/"+self.slug+"/" - composite_location = directory+self.slug+'-geo.tif' - geotiffs = '' - minlat = nil - minlon = nil - maxlat = nil - maxlon = nil - self.placed_warpables.each do |warpable| - warpable.nodes_array.each do |n| - minlat = n.lat if minlat == nil || n.lat < minlat - minlon = n.lon if minlon == nil || n.lon < minlon - maxlat = n.lat if maxlat == nil || n.lat > maxlat - maxlon = n.lon if maxlon == nil || n.lon > maxlon - end - end - first = true - # sort by area; this would be overridden by a provided order - warpables = self.placed_warpables.sort{|a,b|b.poly_area <=> a.poly_area} - warpables.each do |warpable| - geotiffs += ' '+directory+warpable.id.to_s+'-geo.tif' - if first - gdalwarp = "gdalwarp -te "+minlon.to_s+" "+minlat.to_s+" "+maxlon.to_s+" "+maxlat.to_s+" "+directory+warpable.id.to_s+'-geo.tif '+directory+self.slug+'-geo.tif' - first = false - else - gdalwarp = "gdalwarp "+directory+warpable.id.to_s+'-geo.tif '+directory+self.slug+'-geo.tif' - end - puts gdalwarp - system(Gdal.ulimit+gdalwarp) + # we'll eventually replace this with a JavaScript call to initiate an external export process: + def run_export(user, resolution) + key = APP_CONFIG ? APP_CONFIG["google_maps_api_key"] : "AIzaSyAOLUQngEmJv0_zcG1xkGq-CXIPpLQY8iQ" + unless export + export = Export.new({ + :map_id => id + }) end - composite_location - end - - # generates a tileset at Rails.root.to_s/public/tms// - def generate_tiles - google_api_key = APP_CONFIG["google_maps_api_key"] - gdal2tiles = 'gdal2tiles.py -k -t "'+self.slug+'" -g "'+google_api_key+'" '+Rails.root.to_s+'/public/warps/'+self.slug+'/'+self.slug+'-geo.tif '+Rails.root.to_s+'/public/tms/'+self.slug+"/" -# puts gdal2tiles -# puts system('which gdal2tiles.py') - system(Gdal.ulimit+gdal2tiles) - end - - # zips up tiles at Rails.root/public/tms/.zip - def zip_tiles - rmzip = 'cd public/tms/ && rm '+self.slug+'.zip && cd ../../' - system(Gdal.ulimit+rmzip) - zip = 'cd public/tms/ && zip -rq '+self.slug+'.zip '+self.slug+'/ && cd ../../' - # puts zip - # puts system('which gdal2tiles.py') - system(Gdal.ulimit+zip) - end - - def generate_jpg - imageMagick = 'convert -background white -flatten '+Rails.root.to_s+'/public/warps/'+self.slug+'/'+self.slug+'-geo.tif '+Rails.root.to_s+'/public/warps/'+self.slug+'/'+self.slug+'.jpg' - system(Gdal.ulimit+imageMagick) + Exporter.run_export(user, + resolution, + self.export, + self.id, + self.slug, + Rails.root.to_s, + self.average_scale, + self.placed_warpables, + key) end def after_create @@ -384,7 +243,7 @@ def has_tag(tagname) def add_tag(tagname, user) tagname = tagname.downcase unless self.has_tag(tagname) - tag = self.tags.create({ + self.tags.create({ :name => tagname, :user_id => user.id, :map_id => self.id diff --git a/app/models/user.rb b/app/models/user.rb index 1fb0572f6..0c7d0b952 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -42,7 +42,7 @@ def email=(value) end def last_action - map = self.maps.order('updated_at DESC').limit(1).first.updated_at + self.maps.order('updated_at DESC').limit(1).first.updated_at end # Permissions for editing and deleting resources diff --git a/app/models/warpable.rb b/app/models/warpable.rb index cb5da2182..4ec8059e2 100755 --- a/app/models/warpable.rb +++ b/app/models/warpable.rb @@ -141,203 +141,9 @@ def url=(uri) self.uploaded_data = io end - # pixels per meter = pxperm - def generate_perspectival_distort(pxperm,path) - require 'net/http' - - # everything in -working/ can be deleted; - # this is just so we can use the files locally outside of s3 - working_directory = self.working_directory(path) - Dir.mkdir(working_directory) unless (File.exists?(working_directory) && File.directory?(working_directory)) - local_location = working_directory+self.id.to_s+'-'+self.image_file_name.to_s - - directory = self.warps_directory(path) - Dir.mkdir(directory) unless (File.exists?(directory) && File.directory?(directory)) - completed_local_location = directory+self.id.to_s+'.png' - - # everything -masked.png can be deleted - masked_local_location = directory+self.id.to_s+'-masked.png' - # everything -mask.png can be deleted - mask_location = directory+self.id.to_s+'-mask.png' - #completed_local_location = directory+self.id.to_s+'.tif' - # know everything -unwarped can be deleted - geotiff_location = directory+self.id.to_s+'-geo-unwarped.tif' - # everything -geo WITH AN ID could be deleted, but there is a feature request to preserve these - warped_geotiff_location = directory+self.id.to_s+'-geo.tif' - - northmost = self.nodes_array.first.lat - southmost = self.nodes_array.first.lat - westmost = self.nodes_array.first.lon - eastmost = self.nodes_array.first.lon - - self.nodes_array.each do |node| - northmost = node.lat if node.lat > northmost - southmost = node.lat if node.lat < southmost - westmost = node.lon if node.lon < westmost - eastmost = node.lon if node.lon > eastmost - end - - # puts northmost.to_s+','+southmost.to_s+','+westmost.to_s+','+eastmost.to_s - - scale = 20037508.34 - y1 = pxperm*Cartagen.spherical_mercator_lat_to_y(northmost,scale) - x1 = pxperm*Cartagen.spherical_mercator_lon_to_x(westmost,scale) - y2 = pxperm*Cartagen.spherical_mercator_lat_to_y(southmost,scale) - x2 = pxperm*Cartagen.spherical_mercator_lon_to_x(eastmost,scale) - # puts x1.to_s+','+y1.to_s+','+x2.to_s+','+y2.to_s - - # should determine if it's stored in s3 or locally: - if (self.image.url[0..3] == 'http') - Net::HTTP.start('s3.amazonaws.com') { |http| - #Net::HTTP.start('localhost') { |http| - puts (self.image.url) - resp = http.get(self.image.url) - open(local_location, "wb") { |file| - file.write(resp.body) - } - } - else - require "fileutils" - FileUtils.cp(Rails.root.to_s+'/public'+self.image.to_s,local_location) - end - - points = "" - maskpoints = "" - coordinates = "" - first = true - -#EXIF orientation values: -#Value 0th Row 0th Column -#1 top left side -#2 top right side -#3 bottom right side -#4 bottom left side -#5 left side top -#6 right side top -#7 right side bottom -#8 left side bottom - - rotation = (`identify -format %[exif:Orientation] #{local_location}`).to_i - #stdin, stdout, stderr = Open3.popen3('identify -format %[exif:Orientation] #{local_location}') - #rotation = stdout.readlines.first.to_s.to_i - #puts stderr.readlines - - if rotation == 6 - puts 'rotated CCW' - source_corners = source_corners = [[0,self.height],[0,0],[self.width,0],[self.width,self.height]] - elsif rotation == 8 - puts 'rotated CW' - source_corners = [[self.width,0],[self.width,self.height],[0,self.height],[0,0]] - elsif rotation == 3 - puts 'rotated 180 deg' - source_corners = [[self.width,self.height],[0,self.height],[0,0],[self.width,0]] - else - source_corners = [[0,0],[self.width,0],[self.width,self.height],[0,self.height]] - end - - maxdimension = 0 - - self.nodes_array.each do |node| - corner = source_corners.shift - nx1 = corner[0] - ny1 = corner[1] - nx2 = -x1+(pxperm*Cartagen.spherical_mercator_lon_to_x(node.lon,scale)) - ny2 = y1-(pxperm*Cartagen.spherical_mercator_lat_to_y(node.lat,scale)) - - points = points + ' ' unless first - maskpoints = maskpoints + ' ' unless first - points = points + nx1.to_s + ',' + ny1.to_s + ' ' + nx2.to_i.to_s + ',' + ny2.to_i.to_s - maskpoints = maskpoints + nx2.to_i.to_s + ',' + ny2.to_i.to_s - first = false - # we need to find an origin; find northwestern-most point - coordinates = coordinates+' -gcp '+nx2.to_s+', '+ny2.to_s+', '+node.lon.to_s + ', ' + node.lat.to_s - - # identify largest dimension to set canvas size for ImageMagick: - maxdimension = nx1.to_i if maxdimension < nx1.to_i - maxdimension = ny1.to_i if maxdimension < ny1.to_i - maxdimension = nx2.to_i if maxdimension < nx2.to_i - maxdimension = ny2.to_i if maxdimension < ny2.to_i - end - - # close mask polygon: - maskpoints = maskpoints + ' ' - nx2 = -x1+(pxperm*Cartagen.spherical_mercator_lon_to_x(self.nodes_array.first.lon,scale)) - ny2 = y1-(pxperm*Cartagen.spherical_mercator_lat_to_y(self.nodes_array.first.lat,scale)) - maskpoints = maskpoints + nx2.to_i.to_s + ',' + ny2.to_i.to_s - - height = (y1-y2).to_i.to_s - width = (-x1+x2).to_i.to_s - - # http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=11319 - # http://www.imagemagick.org/discourse-server/viewtopic.php?f=3&t=8764 - # read about equalization - # -equalize - # -contrast-stretch 0 - - imageMagick = "convert " - imageMagick += "-contrast-stretch 0 " - imageMagick += local_location+" " - imageMagick += "-crop "+maxdimension.to_i.to_s+"x"+maxdimension.to_i.to_s+"+0+0! " - imageMagick += "-flatten " - imageMagick += "-distort Perspective '"+points+"' " - imageMagick += "-flatten " - if width > height - imageMagick += "-crop "+width+"x"+width+"+0+0\! " - else - imageMagick += "-crop "+height+"x"+height+"+0+0\! " - end - imageMagick += "+repage " - imageMagick += completed_local_location - puts imageMagick - system(Gdal.ulimit+imageMagick) - - # create a mask (later we can blur edges here) - imageMagick2 = 'convert +antialias ' - if width > height - imageMagick2 += "-size "+width+"x"+width+" " - else - imageMagick2 += "-size "+height+"x"+height+" " - end - # attempt at blurred edges in masking, but I've given up, as gdal_merge doesn't seem to respect variable-opacity alpha channels - imageMagick2 += ' xc:none -draw "fill black stroke red stroke-width 30 polyline ' - imageMagick2 += maskpoints + '" ' - imageMagick2 += ' -alpha set -channel A -transparent red -blur 0x8 -channel R -evaluate set 0 +channel '+mask_location - #imageMagick2 += ' xc:none -draw "fill black stroke none polyline ' - #imageMagick2 += maskpoints + '" ' - #imageMagick2 += ' '+mask_location - puts imageMagick2 - system(Gdal.ulimit+imageMagick2) - - imageMagick3 = 'composite '+mask_location+' '+completed_local_location+' -compose DstIn -alpha Set '+masked_local_location - puts imageMagick3 - system(Gdal.ulimit+imageMagick3) - - gdal_translate = "gdal_translate -of GTiff -a_srs EPSG:4326 "+coordinates+' -co "TILED=NO" '+masked_local_location+' '+geotiff_location - puts gdal_translate - system(Gdal.ulimit+gdal_translate) - - #gdalwarp = 'gdalwarp -srcnodata "255" -dstnodata 0 -cblend 30 -of GTiff -t_srs EPSG:4326 '+geotiff_location+' '+warped_geotiff_location - gdalwarp = 'gdalwarp -of GTiff -t_srs EPSG:4326 '+geotiff_location+' '+warped_geotiff_location - puts gdalwarp - system(Gdal.ulimit+gdalwarp) - - # deletions could happen here; do it in distinct method so we can run it independently - self.delete_temp_files(path) - - [x1,y1] - end - - def working_directory(path) - "public/warps/"+path+"-working/" - end - - def warps_directory(path) - "public/warps/"+path+"/" - end - - def delete_temp_files(path) - system('rm -r '+self.working_directory(path)) - system('rm '+self.warps_directory(path)+'*.png') + # TODO: simplify/reduce # of parameters needed here: + def generate_perspectival_distort(pxperm, path) + Exporter.generate_perspectival_distort(pxperm, path, nodes_array, id, image_file_name, image, height, width) end def user_id diff --git a/app/views/images/_new.html.erb b/app/views/images/_new.html.erb index 710e97679..fa6a67c57 100644 --- a/app/views/images/_new.html.erb +++ b/app/views/images/_new.html.erb @@ -16,7 +16,7 @@
- Select images + Select images
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 2e60bd342..a33847a41 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -1,4 +1,4 @@ -