mapcache 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENCE +7 -0
- data/README.rdoc +31 -0
- data/TODO +21 -0
- data/VERSION +1 -0
- data/bin/mapcache +10 -0
- data/lib/download_manager.rb +45 -0
- data/lib/export_dialog.rb +37 -0
- data/lib/map_frame.rb +59 -0
- data/lib/map_panel.rb +140 -0
- data/lib/matrix_manager.rb +189 -0
- data/lib/mgmaps_export.rb +146 -0
- data/lib/tile.rb +82 -0
- data/lib/tile_matrix.rb +73 -0
- metadata +86 -0
    
        data/MIT-LICENCE
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            Copyright (c) 2010 Valentine Bichkovsky
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.rdoc
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            = mapcache
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            == Description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Interactive GoogleMaps browser, caches all downloaded tiles on disk.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            == Install
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              gem install mapcache
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            == Features
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            * download map tiles as you browse the map
         | 
| 14 | 
            +
            * show tiles already on disk for (zoom + 1)..(zoom + 8)
         | 
| 15 | 
            +
            * export downloaded tiles in mgmaps[http://www.mgmaps.com] format
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            == Usage
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            * drag map to pan
         | 
| 20 | 
            +
            * mouse wheel - zoom in/out
         | 
| 21 | 
            +
            * m - toggle coverage
         | 
| 22 | 
            +
            * [] - coverage zoom in/out
         | 
| 23 | 
            +
            * e - export tiles to mgmaps
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            == Files and directories
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            config.yml - your settings are saved in this file
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            <current dir>/maps - this is where map tiles are stored
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            <current dir>/export - this is where export goes to
         | 
    
        data/TODO
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            - менять курсор в зависимости от действий пользователя, использовать "песочные часы" для длительных операций;
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            --------------------------
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - возможность выделять области на карте и сохранять эти выделения. Область выделяется кистью в форме квадрата с привязкой к координатной сетке плиток. Размер квадрата можно менять (увеличивать и уменьшать в 2 раза, примерно как кисть в Фотошопе);
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - экспорт для выделенной области;
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            - массовое скачивание плиток для выделенной области (возможно, с использованием прокси или с генерацией файла для другой программы скачивания);
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - отображение информации - сколько плиток в очереди на скачивание, сколько выделено, сколько потребуется скачать для текущего выделения, прогресс во время скачивания, сколько всего плиток есть для конкретного зума и т.п.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            --------------------------
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - поддержка различных карт, не только простых GoogleMaps;
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            - хранение файлов плиток в одном большом файле (FUSE, tar, zip, ...);
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            - запоминане отсутствующих плиток (404), чтобы не пытаться их каждый раз тянуть из сети. Можно отображать на карте другим цветом;
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.1.0
         | 
    
        data/bin/mapcache
    ADDED
    
    
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            require 'net/http'
         | 
| 2 | 
            +
            require 'thread'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class DownloadManager
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              @queue = Queue.new
         | 
| 7 | 
            +
              @thread = Thread.new do
         | 
| 8 | 
            +
                loop do 
         | 
| 9 | 
            +
                  sleep if @queue.empty?
         | 
| 10 | 
            +
                  new_thread_from_queue
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def self.enqueue(tile)
         | 
| 15 | 
            +
                @queue << tile
         | 
| 16 | 
            +
                @thread.run
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def self.observer=(observer)
         | 
| 20 | 
            +
                @observer = observer
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def self.new_thread_from_queue
         | 
| 26 | 
            +
                Thread.new do
         | 
| 27 | 
            +
                  tile = @queue.pop
         | 
| 28 | 
            +
                  dir = File.dirname(tile.path)
         | 
| 29 | 
            +
                  FileUtils.mkdir_p dir if !File.exist?(dir)
         | 
| 30 | 
            +
                  download(tile)
         | 
| 31 | 
            +
                  tile.load_image
         | 
| 32 | 
            +
                  @observer.tile_loaded(tile)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              def self.download(tile)
         | 
| 37 | 
            +
                uri = URI.parse(tile.url)
         | 
| 38 | 
            +
                req = Net::HTTP::Get.new(uri.path, {"User-Agent" => "mapcache"})
         | 
| 39 | 
            +
                res = Net::HTTP.new(uri.host).start {|http| http.request(req)}
         | 
| 40 | 
            +
                if res.code == "200"
         | 
| 41 | 
            +
                  File.open(tile.path, 'wb') {|f| f.write(res.body)}
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            class ExportDialog < Wx::Dialog
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              def tiles_per_file
         | 
| 4 | 
            +
                @tpf_choice.string_selection.to_i
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def hash_size
         | 
| 8 | 
            +
                @hs_choice.string_selection.to_i
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
              
         | 
| 11 | 
            +
              def initialize(parent)
         | 
| 12 | 
            +
                super(parent, :title => 'Export')
         | 
| 13 | 
            +
                grid = Wx::FlexGridSizer.new(2, 2, 5, 5)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                label = Wx::StaticText.new(self, :label => "Tiles per file")
         | 
| 16 | 
            +
                @tpf_choice = Wx::Choice.new(self, :choices => %w(1 8 16 32 64 256))
         | 
| 17 | 
            +
                grid.add(label, 0, Wx::ALIGN_CENTER_VERTICAL|Wx::ALL, 5)
         | 
| 18 | 
            +
                grid.add(@tpf_choice, 0, Wx::GROW|Wx::ALL, 5)
         | 
| 19 | 
            +
                
         | 
| 20 | 
            +
                label = Wx::StaticText.new(self, :label => "Hash size")
         | 
| 21 | 
            +
                @hs_choice = Wx::Choice.new(self, :choices => %w(1 5 11 23 47 97))    
         | 
| 22 | 
            +
                grid.add(label, 0, Wx::ALIGN_CENTER_VERTICAL|Wx::ALL, 5)
         | 
| 23 | 
            +
                grid.add(@hs_choice, 0, Wx::GROW|Wx::ALL, 5)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                sizer = Wx::BoxSizer.new(Wx::VERTICAL)
         | 
| 26 | 
            +
                sizer.add(grid, 0, Wx::ALL, 5)
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                button_sizer = create_button_sizer(Wx::OK|Wx::CANCEL)
         | 
| 29 | 
            +
                sizer.add(button_sizer, 0, Wx::ALIGN_CENTER_VERTICAL|Wx::ALL, 5)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                self.sizer = sizer
         | 
| 32 | 
            +
                sizer.fit(self)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                evt_choice(@tpf_choice) {|event| @hs_choice.string_selection = '1' if tiles_per_file != 1}
         | 
| 35 | 
            +
                evt_choice(@hs_choice) {|event| @tpf_choice.string_selection = '1' if hash_size != 1}
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
    
        data/lib/map_frame.rb
    ADDED
    
    | @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class MapFrame < Wx::Frame
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              DEFAULT_CONFIG = {'width' => 512, 'height' => 512, 'left' => 0, 'top' => 0,
         | 
| 6 | 
            +
                'map' => {'left_col' => 288, 'top_row' => 155, 'offset_x' => 0, 'offset_y' => 0, 
         | 
| 7 | 
            +
                  'zoom' => 9, 'show_cov' => true, 'cov_zoom' => 10} }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def initialize
         | 
| 10 | 
            +
                config = load_config
         | 
| 11 | 
            +
                super(nil, :title => "mapcache",
         | 
| 12 | 
            +
                      :pos => [config['left'], config['top']],
         | 
| 13 | 
            +
                      :size => [config['width'], config['height']] )
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                @status = Wx::StatusBar.new(self)
         | 
| 16 | 
            +
                @status.fields_count = 3
         | 
| 17 | 
            +
                @status.set_status_text("press F1 for help", 2)
         | 
| 18 | 
            +
                self.status_bar = @status
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                box = Wx::HBoxSizer.new
         | 
| 21 | 
            +
                @panel = MapPanel.new(self, config['map'])
         | 
| 22 | 
            +
                box.add(@panel, 1, Wx::EXPAND)
         | 
| 23 | 
            +
                self.sizer = box
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                evt_close do |event| 
         | 
| 26 | 
            +
                  save_config
         | 
| 27 | 
            +
                  destroy
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                show
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              def set_zoom(value)
         | 
| 34 | 
            +
                @status.set_status_text("zoom: #{value}", 0)
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def set_coverage_zoom(value)
         | 
| 38 | 
            +
                text = value ? "coverage: #{value}" : ""
         | 
| 39 | 
            +
                @status.set_status_text(text, 1)
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              private 
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def load_config
         | 
| 45 | 
            +
                File.exist?('config.yml') ? YAML::load_file('config.yml') : DEFAULT_CONFIG
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def save_config
         | 
| 49 | 
            +
                config = {
         | 
| 50 | 
            +
                  'left' => self.position.x,
         | 
| 51 | 
            +
                  'top' => self.position.y,
         | 
| 52 | 
            +
                  'width' => self.size.width,
         | 
| 53 | 
            +
                  'height' => self.size.height,
         | 
| 54 | 
            +
                  'map' => @panel.config
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
               open('config.yml', 'w') { |f| YAML.dump(config, f) }
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            end
         | 
    
        data/lib/map_panel.rb
    ADDED
    
    | @@ -0,0 +1,140 @@ | |
| 1 | 
            +
            class MapPanel < Wx::Panel
         | 
| 2 | 
            +
              
         | 
| 3 | 
            +
              def initialize(parent, config)
         | 
| 4 | 
            +
                super(parent, :style => Wx::NO_BORDER)
         | 
| 5 | 
            +
                @pan = false
         | 
| 6 | 
            +
                DownloadManager.observer = self
         | 
| 7 | 
            +
                @mgr = MatrixManager.new(self.size.width, self.size.height, config)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                evt_paint {|event| draw_map}
         | 
| 10 | 
            +
                
         | 
| 11 | 
            +
                evt_size {|event| resize_map(event.size.width, event.size.height) }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                evt_char do |event|
         | 
| 14 | 
            +
                  case event.key_code
         | 
| 15 | 
            +
                    when ?m : toggle_coverage
         | 
| 16 | 
            +
                    when ?[ : coverage_zoom_out
         | 
| 17 | 
            +
                    when ?] : coverage_zoom_in
         | 
| 18 | 
            +
                    when ?e : export_dialog
         | 
| 19 | 
            +
                    when Wx::K_F1: about_box
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                evt_left_down do |event| 
         | 
| 24 | 
            +
                  pan_start(event.x, event.y)
         | 
| 25 | 
            +
                  event.skip
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                evt_left_up {|event| pan_end}
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                evt_motion {|event| pan(event.x, event.y) }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                evt_mousewheel do |event|
         | 
| 33 | 
            +
                  if event.wheel_rotation > 0
         | 
| 34 | 
            +
                    zoom_in(event.x, event.y)
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    zoom_out(event.x, event.y)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                parent.set_zoom(@mgr.zoom)
         | 
| 41 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                set_focus
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def config
         | 
| 47 | 
            +
                @mgr.config
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def tile_loaded(tile)
         | 
| 51 | 
            +
                self.refresh
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              private
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def draw_map
         | 
| 57 | 
            +
                paint {|dc| @mgr.draw(dc) }
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def toggle_coverage
         | 
| 61 | 
            +
                draw_with_hourglass { @mgr.toggle_coverage }
         | 
| 62 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def zoom_in(x, y)
         | 
| 66 | 
            +
                draw_with_hourglass { @mgr.zoom_in(x, y) }
         | 
| 67 | 
            +
                parent.set_zoom(@mgr.zoom)
         | 
| 68 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def zoom_out(x, y)
         | 
| 72 | 
            +
                draw_with_hourglass { @mgr.zoom_out(x, y) }
         | 
| 73 | 
            +
                parent.set_zoom(@mgr.zoom)
         | 
| 74 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def coverage_zoom_in
         | 
| 78 | 
            +
                draw_with_hourglass { @mgr.coverage_zoom_in }
         | 
| 79 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              def coverage_zoom_out
         | 
| 83 | 
            +
                draw_with_hourglass { @mgr.coverage_zoom_out }
         | 
| 84 | 
            +
                parent.set_coverage_zoom(@mgr.cov_value)
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              def export_dialog
         | 
| 88 | 
            +
                d = ExportDialog.new(parent)
         | 
| 89 | 
            +
                if d.show_modal == Wx::ID_OK
         | 
| 90 | 
            +
                  MGMapsExport.export(d.tiles_per_file, d.hash_size)
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              def resize_map(width, height)
         | 
| 95 | 
            +
                @mgr.resize(width, height)
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              def pan_start(x, y)
         | 
| 99 | 
            +
                @pan = true
         | 
| 100 | 
            +
                @start_x = x
         | 
| 101 | 
            +
                @start_y = y
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              def pan_end
         | 
| 105 | 
            +
                @pan = false
         | 
| 106 | 
            +
              end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
              def pan(x, y)
         | 
| 109 | 
            +
                if @pan
         | 
| 110 | 
            +
                  @mgr.pan(x - @start_x, y - @start_y)
         | 
| 111 | 
            +
                  @start_x = x
         | 
| 112 | 
            +
                  @start_y = y
         | 
| 113 | 
            +
                  draw_map
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              def draw_with_hourglass
         | 
| 118 | 
            +
                self.cursor = Wx::HOURGLASS_CURSOR
         | 
| 119 | 
            +
                yield
         | 
| 120 | 
            +
                draw_map
         | 
| 121 | 
            +
                self.cursor = Wx::NULL_CURSOR    
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              def about_box
         | 
| 125 | 
            +
                box = Wx::AboutDialogInfo.new
         | 
| 126 | 
            +
                box.name = "mapcache"
         | 
| 127 | 
            +
                box.version = APP_VERSION
         | 
| 128 | 
            +
                box.description = "
         | 
| 129 | 
            +
                  click and drag to pan
         | 
| 130 | 
            +
                  mouse wheel - zoom in/out
         | 
| 131 | 
            +
                  m - toggle coverage
         | 
| 132 | 
            +
                  [] - coverage zoom in/out
         | 
| 133 | 
            +
                  e - export to mgmaps
         | 
| 134 | 
            +
                  for more info visit http://wiki.github.com/vbichkovsky/mapcache/"
         | 
| 135 | 
            +
                box.add_developer 'Valentine Bichkovsky'
         | 
| 136 | 
            +
                box.web_site = "http://wiki.github.com/vbichkovsky/mapcache/"
         | 
| 137 | 
            +
                Wx::about_box(box)
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            end
         | 
| @@ -0,0 +1,189 @@ | |
| 1 | 
            +
            TILE_WIDTH = 256
         | 
| 2 | 
            +
            MAX_ZOOM = 19
         | 
| 3 | 
            +
            MAX_ZOOM_DIFF = 8
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class MatrixManager
         | 
| 6 | 
            +
              attr_reader :top_row, :left_col, :offset_x, :offset_y, :zoom, :show_cov, :cov_zoom
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def initialize(width, height, config)
         | 
| 9 | 
            +
                @show_cov = config['show_cov']
         | 
| 10 | 
            +
                @cov_zoom = config['cov_zoom']
         | 
| 11 | 
            +
                @zoom = config['zoom']
         | 
| 12 | 
            +
                @left_col = config['left_col']
         | 
| 13 | 
            +
                @top_row = config['top_row']
         | 
| 14 | 
            +
                @offset_x = config['offset_x']
         | 
| 15 | 
            +
                @offset_y = config['offset_y']
         | 
| 16 | 
            +
                @width = width
         | 
| 17 | 
            +
                @height = height
         | 
| 18 | 
            +
                create_matrix
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def config
         | 
| 22 | 
            +
                result = {}
         | 
| 23 | 
            +
                %w(left_col top_row offset_x offset_y zoom show_cov cov_zoom).each do |attr|
         | 
| 24 | 
            +
                  result[attr] = self.send(attr)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                result
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def zoom_in(x, y)
         | 
| 30 | 
            +
                if @zoom < MAX_ZOOM
         | 
| 31 | 
            +
                  @zoom += 1
         | 
| 32 | 
            +
                  @cov_zoom = zoom + 1 if cov_zoom - zoom < 1 
         | 
| 33 | 
            +
                  @offset_x, @left_col = calc_start_and_offset_zoom_in(@left_col, x, @offset_x, @width)
         | 
| 34 | 
            +
                  @offset_y, @top_row = calc_start_and_offset_zoom_in(@top_row, y, @offset_y, @height)
         | 
| 35 | 
            +
                  create_matrix
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def zoom_out(x, y)
         | 
| 40 | 
            +
                if @zoom > 0
         | 
| 41 | 
            +
                  @zoom -= 1
         | 
| 42 | 
            +
                  @cov_zoom = zoom + MAX_ZOOM_DIFF if cov_zoom - zoom > MAX_ZOOM_DIFF
         | 
| 43 | 
            +
                  @offset_x, @left_col = calc_start_and_offset_zoom_out(@left_col, x, @offset_x, @width)
         | 
| 44 | 
            +
                  @offset_y, @top_row = calc_start_and_offset_zoom_out(@top_row, y, @offset_y, @height)
         | 
| 45 | 
            +
                  create_matrix
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def draw(dc)
         | 
| 50 | 
            +
                @matrix.each do |col, row, tile|
         | 
| 51 | 
            +
                  tile.draw(dc, TILE_WIDTH * (col - 1) + @offset_x, TILE_WIDTH * (row - 1) + @offset_y)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def toggle_coverage
         | 
| 56 | 
            +
                @show_cov = !show_cov
         | 
| 57 | 
            +
                recreate_coverage
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def coverage_zoom_in
         | 
| 61 | 
            +
                if cov_zoom - zoom < MAX_ZOOM_DIFF
         | 
| 62 | 
            +
                  @cov_zoom += 1
         | 
| 63 | 
            +
                  recreate_coverage
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              def coverage_zoom_out
         | 
| 68 | 
            +
                if cov_zoom - zoom > 1
         | 
| 69 | 
            +
                  @cov_zoom -= 1
         | 
| 70 | 
            +
                  recreate_coverage
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              def pan(dx, dy)
         | 
| 75 | 
            +
                @offset_x += dx
         | 
| 76 | 
            +
                @offset_y += dy
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                if @offset_x < 0
         | 
| 79 | 
            +
                  @offset_x = TILE_WIDTH + @offset_x
         | 
| 80 | 
            +
                  @left_col += 1
         | 
| 81 | 
            +
                  @matrix.shift_left(get_tiles(:column, :last) )
         | 
| 82 | 
            +
                elsif @offset_x >= TILE_WIDTH
         | 
| 83 | 
            +
                  @offset_x -= TILE_WIDTH
         | 
| 84 | 
            +
                  @left_col -= 1
         | 
| 85 | 
            +
                  @matrix.shift_right(get_tiles(:column, :first) )
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                if @offset_y < 0
         | 
| 89 | 
            +
                  @offset_y = TILE_WIDTH + @offset_y
         | 
| 90 | 
            +
                  @top_row += 1
         | 
| 91 | 
            +
                  @matrix.shift_up(get_tiles(:row, :last) )
         | 
| 92 | 
            +
                elsif @offset_y >= TILE_WIDTH
         | 
| 93 | 
            +
                  @offset_y -= TILE_WIDTH
         | 
| 94 | 
            +
                  @top_row -= 1
         | 
| 95 | 
            +
                  @matrix.shift_down(get_tiles(:row, :first) )
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              def resize(viewport_width, viewport_height)
         | 
| 100 | 
            +
                @width = viewport_width
         | 
| 101 | 
            +
                @height = viewport_height
         | 
| 102 | 
            +
                new_matrix_width = (viewport_width.to_f / TILE_WIDTH).ceil + 2
         | 
| 103 | 
            +
                new_matrix_height = (viewport_height.to_f / TILE_WIDTH).ceil + 2
         | 
| 104 | 
            +
                if new_matrix_width < @matrix.width || new_matrix_height < @matrix.height
         | 
| 105 | 
            +
                  @matrix.reduce(new_matrix_width, new_matrix_height)
         | 
| 106 | 
            +
                else
         | 
| 107 | 
            +
                  if new_matrix_width > @matrix.width
         | 
| 108 | 
            +
                    (@left_col + @matrix.width..@left_col + new_matrix_width - 1).each do |col|
         | 
| 109 | 
            +
                      column = []
         | 
| 110 | 
            +
                      (@top_row..@top_row + @matrix.height - 1).each do |row|
         | 
| 111 | 
            +
                        column << get_tile(col, row, @zoom)
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                      @matrix.add_column(column)
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                  if new_matrix_height > @matrix.height
         | 
| 117 | 
            +
                    (@top_row + @matrix.height..@top_row + new_matrix_height - 1).each do |r|
         | 
| 118 | 
            +
                      row = []
         | 
| 119 | 
            +
                      (@left_col..@left_col + new_matrix_width - 1).each do |col|
         | 
| 120 | 
            +
                        row << get_tile(col, r, @zoom)
         | 
| 121 | 
            +
                      end
         | 
| 122 | 
            +
                      @matrix.add_row(row)
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              def cov_value
         | 
| 129 | 
            +
                show_cov && cov_zoom
         | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
              private
         | 
| 133 | 
            +
             | 
| 134 | 
            +
              def calc_start_and_offset_zoom_in(start_idx, cursor_pos, offset, viewport_size)
         | 
| 135 | 
            +
                middle_tile_idx = start_idx * 2 + 2 + (cursor_pos - offset).to_i / (TILE_WIDTH / 2)
         | 
| 136 | 
            +
                from_edge = 2 * ( (cursor_pos - offset).to_i % (TILE_WIDTH / 2) )
         | 
| 137 | 
            +
                calc_offset_and_first_idx(middle_tile_idx, viewport_size, from_edge)
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              def calc_start_and_offset_zoom_out(start_idx, cursor_pos, offset, viewport_size)
         | 
| 141 | 
            +
                cursor_tile_idx = start_idx + (cursor_pos - offset).to_i / TILE_WIDTH + 1
         | 
| 142 | 
            +
                middle_tile_idx = cursor_tile_idx / 2
         | 
| 143 | 
            +
                from_edge = ( (cursor_pos - offset).to_i % TILE_WIDTH + 
         | 
| 144 | 
            +
                              (cursor_tile_idx.even? ? 0 : TILE_WIDTH) ) / 2
         | 
| 145 | 
            +
                calc_offset_and_first_idx(middle_tile_idx, viewport_size, from_edge)
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
              def calc_offset_and_first_idx(middle_idx, viewport_size, from_tile_edge)
         | 
| 149 | 
            +
                [ (viewport_size / 2 - from_tile_edge) % TILE_WIDTH,
         | 
| 150 | 
            +
                  middle_idx - ( (viewport_size / 2 - from_tile_edge) / TILE_WIDTH ) - 1 ]
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              def create_matrix
         | 
| 154 | 
            +
                @matrix = TileMatrix.new
         | 
| 155 | 
            +
                @matrix[0,0] = get_tile(@left_col, @top_row, @zoom)
         | 
| 156 | 
            +
                resize(@width, @height)
         | 
| 157 | 
            +
              end    
         | 
| 158 | 
            +
             | 
| 159 | 
            +
              def get_tiles(what, which)
         | 
| 160 | 
            +
                size = (what == :row ? @matrix.width - 1 : @matrix.height - 1)
         | 
| 161 | 
            +
                col = @left_col
         | 
| 162 | 
            +
                row = @top_row
         | 
| 163 | 
            +
                if which == :last
         | 
| 164 | 
            +
                  what == :column ? col += @matrix.width - 1 : row += @matrix.height - 1
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
                (0..size).map do |index|
         | 
| 167 | 
            +
                  get_tile(col + (what == :row ? index : 0),
         | 
| 168 | 
            +
                           row + (what == :column ? index : 0), @zoom)
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
              end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
              def get_tile(col, row, zoom)
         | 
| 173 | 
            +
                Tile.new(wraparound(col, zoom), wraparound(row, zoom), zoom, cov_value)
         | 
| 174 | 
            +
              end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
              def wraparound(value, zoom)
         | 
| 177 | 
            +
                max = 2 ** zoom
         | 
| 178 | 
            +
                value = max - (value.abs % max) if value < 0
         | 
| 179 | 
            +
                value = value % max if value >= max
         | 
| 180 | 
            +
                value
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              def recreate_coverage
         | 
| 184 | 
            +
                @matrix.each do |col, row, tile|
         | 
| 185 | 
            +
                  tile.create_coverage(show_cov && cov_zoom)
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
              end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
            end
         | 
| @@ -0,0 +1,146 @@ | |
| 1 | 
            +
            require 'pathname'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class MGMapsExport
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              MAPS_ROOT = Pathname.new("maps")
         | 
| 6 | 
            +
              EXPORT_ROOT = Pathname.new("export")
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.export(tiles_per_file, hash_size)
         | 
| 9 | 
            +
                if EXPORT_ROOT.exist?
         | 
| 10 | 
            +
                  error_dlg('Cannot export: export directory already exists!')
         | 
| 11 | 
            +
                  return
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                files = Dir['maps/*/*.mgm']
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                catch :cancelled do
         | 
| 17 | 
            +
                  if tiles_per_file > 1
         | 
| 18 | 
            +
                    export_as_mtpf(files, tiles_per_file)
         | 
| 19 | 
            +
                  elsif hash_size > 1
         | 
| 20 | 
            +
                    export_as_hashed(files, hash_size)
         | 
| 21 | 
            +
                  else
         | 
| 22 | 
            +
                    export_as_copy(files)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  create_conf_file(tiles_per_file, hash_size)
         | 
| 26 | 
            +
                  success_msg
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def self.error_dlg(msg)
         | 
| 33 | 
            +
                  Wx::MessageDialog.new(Wx::THE_APP.top_window, msg, 'Export',
         | 
| 34 | 
            +
                                        Wx::OK|Wx::ICON_ERROR).show_modal
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def self.success_msg
         | 
| 38 | 
            +
                  Wx::MessageDialog.new(Wx::THE_APP.top_window, 'Export completed', 'Export',
         | 
| 39 | 
            +
                                        Wx::OK|Wx::ICON_INFORMATION).show_modal
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def self.confirm(msg)
         | 
| 43 | 
            +
                  Wx::MessageDialog.new(Wx::THE_APP.top_window, msg, 'Export',
         | 
| 44 | 
            +
                                        Wx::YES_NO|Wx::ICON_QUESTION).show_modal == Wx::ID_YES
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def self.create_conf_file(tiles_per_file, hash_size)
         | 
| 48 | 
            +
                File.open('export/cache.conf', 'w') do |file|
         | 
| 49 | 
            +
                  file.puts "version=3"
         | 
| 50 | 
            +
                  file.puts "tiles_per_file=#{tiles_per_file}"
         | 
| 51 | 
            +
                  file.puts "hash_size=#{hash_size}"
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def self.export_each_file(files)
         | 
| 56 | 
            +
                progress = Wx::ProgressDialog.new('Export', 'Processing files...', 
         | 
| 57 | 
            +
                                                  files.size, Wx::THE_APP.top_window,
         | 
| 58 | 
            +
                                                  Wx::PD_AUTO_HIDE | Wx::PD_APP_MODAL | 
         | 
| 59 | 
            +
                                                  Wx::PD_CAN_ABORT | Wx::PD_REMAINING_TIME)
         | 
| 60 | 
            +
                files_done = 0
         | 
| 61 | 
            +
                files.each do |file|
         | 
| 62 | 
            +
                  yield file
         | 
| 63 | 
            +
                  files_done += 1
         | 
| 64 | 
            +
                  if !progress.update(files_done)
         | 
| 65 | 
            +
                    if confirm('Cancel export?') 
         | 
| 66 | 
            +
                      progress.destroy
         | 
| 67 | 
            +
                      throw :cancelled
         | 
| 68 | 
            +
                    else
         | 
| 69 | 
            +
                      progress.resume
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def self.export_as_copy(files)
         | 
| 76 | 
            +
                export_each_file(files) do |file|
         | 
| 77 | 
            +
                  src = Pathname.new(file)
         | 
| 78 | 
            +
                  dest = EXPORT_ROOT + src.relative_path_from(MAPS_ROOT)
         | 
| 79 | 
            +
                  FileUtils.mkdir_p dest.dirname
         | 
| 80 | 
            +
                  FileUtils.cp file, dest
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              def self.export_as_hashed(files, hash_size)
         | 
| 85 | 
            +
                export_each_file(files) do |file|
         | 
| 86 | 
            +
                  src = Pathname.new(file)
         | 
| 87 | 
            +
                  x, y = src.basename('.mgm').to_s.split('_').map{|n| n.to_i}
         | 
| 88 | 
            +
                  subdir = Pathname.new( ((x * 256 + y) % hash_size).to_s )
         | 
| 89 | 
            +
                  zoomdir = src.relative_path_from(MAPS_ROOT).dirname
         | 
| 90 | 
            +
                  dest = EXPORT_ROOT + zoomdir + subdir + src.basename
         | 
| 91 | 
            +
                  FileUtils.mkdir_p dest.dirname
         | 
| 92 | 
            +
                  FileUtils.cp file, dest
         | 
| 93 | 
            +
                end    
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              def self.export_as_mtpf(files, tiles_per_file)
         | 
| 97 | 
            +
                tpf_x, tpf_y = tpf_xy(tiles_per_file)
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                export_each_file(files) do |file|
         | 
| 100 | 
            +
                  src = Pathname.new(file)
         | 
| 101 | 
            +
                  x, y = src.basename('.mgm').to_s.split('_').map{|n| n.to_i}
         | 
| 102 | 
            +
                  dest_x = x / tpf_x
         | 
| 103 | 
            +
                  dest_y = y / tpf_y
         | 
| 104 | 
            +
                  rel_x = x % tpf_x
         | 
| 105 | 
            +
                  rel_y = y % tpf_y
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  zoomdir = src.relative_path_from(MAPS_ROOT).dirname
         | 
| 108 | 
            +
                  dest = EXPORT_ROOT + zoomdir + Pathname.new("#{dest_x}_#{dest_y}.mgm")
         | 
| 109 | 
            +
                  blank_mtpf_file(dest, tiles_per_file) if !File.exists?(dest)
         | 
| 110 | 
            +
                  add_tile_to_file(src, dest, rel_x, rel_y)
         | 
| 111 | 
            +
                end    
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              def self.tpf_xy(tpf)
         | 
| 115 | 
            +
                log2 = (Math.log(tpf) / Math.log(2)).to_i
         | 
| 116 | 
            +
                if log2.even?
         | 
| 117 | 
            +
                  tpf_x = Math.sqrt(tpf).to_i
         | 
| 118 | 
            +
                  tpf_y = tpf_x
         | 
| 119 | 
            +
                else
         | 
| 120 | 
            +
                  tpf_x = Math.sqrt(tpf * 2).to_i
         | 
| 121 | 
            +
                  tpf_y = tpf_x / 2
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
                [tpf_x, tpf_y]
         | 
| 124 | 
            +
              end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
              def self.blank_mtpf_file(dest, tiles_per_file)
         | 
| 127 | 
            +
                FileUtils.mkdir_p dest.dirname if !dest.dirname.exist?
         | 
| 128 | 
            +
                File.open(dest, 'w') { |f| f.write 0.chr * (2 + 6 * tiles_per_file) }
         | 
| 129 | 
            +
              end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
              def self.add_tile_to_file(src, dest, rel_x, rel_y)
         | 
| 132 | 
            +
                File.open(dest, 'r+') do |file|
         | 
| 133 | 
            +
                  count = file.read(2).unpack('n').first + 1
         | 
| 134 | 
            +
                  file.seek(0, IO::SEEK_END)
         | 
| 135 | 
            +
                  file.write(IO.read(src))
         | 
| 136 | 
            +
                  pos = file.pos
         | 
| 137 | 
            +
                  file.rewind
         | 
| 138 | 
            +
                  file.write [count].pack('n')
         | 
| 139 | 
            +
                  file.pos = 2 + 6 * (count - 1)
         | 
| 140 | 
            +
                  file.write rel_x.chr
         | 
| 141 | 
            +
                  file.write rel_y.chr
         | 
| 142 | 
            +
                  file.write [pos].pack('N')
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            end
         | 
    
        data/lib/tile.rb
    ADDED
    
    | @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            require 'fileutils'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Tile
         | 
| 4 | 
            +
              attr_reader :col, :row, :zoom
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              def initialize(col, row, zoom, cov_zoom)
         | 
| 7 | 
            +
                @col = col
         | 
| 8 | 
            +
                @row = row
         | 
| 9 | 
            +
                @zoom = zoom
         | 
| 10 | 
            +
                if File.exist?(path)
         | 
| 11 | 
            +
                  load_image
         | 
| 12 | 
            +
                else
         | 
| 13 | 
            +
                  DownloadManager.enqueue(self)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                create_coverage(cov_zoom)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def create_coverage(cov_zoom)
         | 
| 19 | 
            +
                if !cov_zoom
         | 
| 20 | 
            +
                  @mask = nil
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  @mask = Wx::Bitmap.new(TILE_WIDTH, TILE_WIDTH)
         | 
| 23 | 
            +
                  @mask.draw do |dc| 
         | 
| 24 | 
            +
                    dc.pen = Wx::TRANSPARENT_PEN
         | 
| 25 | 
            +
                    dc.brush = Wx::WHITE_BRUSH
         | 
| 26 | 
            +
                    dc.draw_rectangle(0, 0, TILE_WIDTH, TILE_WIDTH)
         | 
| 27 | 
            +
                    draw_subtiles(dc, cov_zoom)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def load_image
         | 
| 33 | 
            +
                image = Wx::Image.new(path, Wx::BITMAP_TYPE_PNG)
         | 
| 34 | 
            +
                @bitmap = Wx::Bitmap.from_image(image)
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def draw(dc, x, y)
         | 
| 38 | 
            +
                dc.pen = Wx::TRANSPARENT_PEN
         | 
| 39 | 
            +
                dc.logical_function = Wx::COPY
         | 
| 40 | 
            +
                if @bitmap
         | 
| 41 | 
            +
                  dc.draw_bitmap(@bitmap, x, y, false)
         | 
| 42 | 
            +
                else
         | 
| 43 | 
            +
                  dc.brush = Wx::GREY_BRUSH
         | 
| 44 | 
            +
                  dc.draw_rectangle(x, y, TILE_WIDTH, TILE_WIDTH)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                if @mask
         | 
| 47 | 
            +
                  dc.logical_function = Wx::AND
         | 
| 48 | 
            +
                  dc.draw_bitmap(@mask, x, y, false)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def url
         | 
| 53 | 
            +
                server = (@col + 2 * @row) % 4
         | 
| 54 | 
            +
                galileo = "Galileo"[0, (3 * @col + @row) % 8]
         | 
| 55 | 
            +
                "http://mt#{server}.google.com/vt/lyrs=m@115&hl=en&x=#{@col}&y=#{@row}&z=#{@zoom}&s=#{galileo}"
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def path
         | 
| 59 | 
            +
                "maps/GoogleMap_#{zoom}/#{col}_#{row}.mgm"
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              private
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              def draw_subtiles(dc, cov_zoom)
         | 
| 65 | 
            +
                dc.brush = Wx::GREEN_BRUSH
         | 
| 66 | 
            +
                diff = cov_zoom - zoom
         | 
| 67 | 
            +
                scale = 2 ** diff
         | 
| 68 | 
            +
                Dir["maps/GoogleMap_#{cov_zoom}/*.mgm"].each do |file|
         | 
| 69 | 
            +
                  cov_col, cov_row = File.basename(file, '.mgm').split('_')
         | 
| 70 | 
            +
                  cov_col = cov_col.to_i
         | 
| 71 | 
            +
                  cov_row = cov_row.to_i
         | 
| 72 | 
            +
                  if ((cov_col / scale) == col) && ((cov_row / scale) == row)
         | 
| 73 | 
            +
                    subcol = cov_col % scale
         | 
| 74 | 
            +
                    subrow = cov_row % scale
         | 
| 75 | 
            +
                    dc.draw_rectangle(subcol * TILE_WIDTH / scale, 
         | 
| 76 | 
            +
                                      subrow * TILE_WIDTH / scale, 
         | 
| 77 | 
            +
                                      TILE_WIDTH / scale, TILE_WIDTH / scale)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end    
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            end
         | 
    
        data/lib/tile_matrix.rb
    ADDED
    
    | @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            class TileMatrix
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              def initialize
         | 
| 4 | 
            +
                @columns = []
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def [](col, row)
         | 
| 8 | 
            +
                @columns[col][row]
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def[]=(col, row, value)
         | 
| 12 | 
            +
                @columns[col] ||= []
         | 
| 13 | 
            +
                @columns[col][row] = value
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def width
         | 
| 17 | 
            +
                @columns.size
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def height
         | 
| 21 | 
            +
                @columns[0].size
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def each
         | 
| 25 | 
            +
                @columns.each_with_index do |row, col_idx|
         | 
| 26 | 
            +
                  row.each_with_index do |value, row_idx|
         | 
| 27 | 
            +
                    yield col_idx, row_idx, value
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def shift_left(new_column)
         | 
| 33 | 
            +
                @columns.shift
         | 
| 34 | 
            +
                @columns.push(new_column)
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def shift_right(new_column)
         | 
| 38 | 
            +
                @columns.delete_at(@columns.length - 1)
         | 
| 39 | 
            +
                @columns.unshift(new_column)
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def shift_up(new_row)
         | 
| 43 | 
            +
                @columns.each_with_index do |c, i|
         | 
| 44 | 
            +
                  c.shift
         | 
| 45 | 
            +
                  c.push(new_row[i])
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def shift_down(new_row)
         | 
| 50 | 
            +
                @columns.each_with_index do |c, i|
         | 
| 51 | 
            +
                  c.delete_at(c.length - 1)
         | 
| 52 | 
            +
                  c.unshift(new_row[i])
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def reduce(width, height)
         | 
| 57 | 
            +
                @columns.slice!(width..@columns.length - 1)
         | 
| 58 | 
            +
                @columns.each do |c|
         | 
| 59 | 
            +
                  c.slice!(height..c.length - 1)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def add_column(new_column)
         | 
| 64 | 
            +
                @columns << new_column
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              def add_row(new_row)
         | 
| 68 | 
            +
                @columns.each_with_index do |c, i|
         | 
| 69 | 
            +
                  c << new_row[i]
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: mapcache
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors: 
         | 
| 7 | 
            +
            - Valentine Bichkovsky
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            date: 2010-02-04 00:00:00 +02:00
         | 
| 13 | 
            +
            default_executable: mapcache
         | 
| 14 | 
            +
            dependencies: 
         | 
| 15 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 16 | 
            +
              name: wxruby
         | 
| 17 | 
            +
              type: :runtime
         | 
| 18 | 
            +
              version_requirement: 
         | 
| 19 | 
            +
              version_requirements: !ruby/object:Gem::Requirement 
         | 
| 20 | 
            +
                requirements: 
         | 
| 21 | 
            +
                - - ">="
         | 
| 22 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 23 | 
            +
                    version: 2.0.1
         | 
| 24 | 
            +
                version: 
         | 
| 25 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 26 | 
            +
              name: rspec
         | 
| 27 | 
            +
              type: :development
         | 
| 28 | 
            +
              version_requirement: 
         | 
| 29 | 
            +
              version_requirements: !ruby/object:Gem::Requirement 
         | 
| 30 | 
            +
                requirements: 
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 33 | 
            +
                    version: "0"
         | 
| 34 | 
            +
                version: 
         | 
| 35 | 
            +
            description: 
         | 
| 36 | 
            +
            email: valentin@livas.lv
         | 
| 37 | 
            +
            executables: 
         | 
| 38 | 
            +
            - mapcache
         | 
| 39 | 
            +
            extensions: []
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            extra_rdoc_files: 
         | 
| 42 | 
            +
            - README.rdoc
         | 
| 43 | 
            +
            files: 
         | 
| 44 | 
            +
            - MIT-LICENCE
         | 
| 45 | 
            +
            - README.rdoc
         | 
| 46 | 
            +
            - TODO
         | 
| 47 | 
            +
            - VERSION
         | 
| 48 | 
            +
            - bin/mapcache
         | 
| 49 | 
            +
            - lib/download_manager.rb
         | 
| 50 | 
            +
            - lib/export_dialog.rb
         | 
| 51 | 
            +
            - lib/map_frame.rb
         | 
| 52 | 
            +
            - lib/map_panel.rb
         | 
| 53 | 
            +
            - lib/matrix_manager.rb
         | 
| 54 | 
            +
            - lib/mgmaps_export.rb
         | 
| 55 | 
            +
            - lib/tile.rb
         | 
| 56 | 
            +
            - lib/tile_matrix.rb
         | 
| 57 | 
            +
            has_rdoc: true
         | 
| 58 | 
            +
            homepage: http://wiki.github.com/vbichkovsky/mapcache/
         | 
| 59 | 
            +
            licenses: []
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            post_install_message: 
         | 
| 62 | 
            +
            rdoc_options: 
         | 
| 63 | 
            +
            - --charset=UTF-8
         | 
| 64 | 
            +
            require_paths: 
         | 
| 65 | 
            +
            - lib
         | 
| 66 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 67 | 
            +
              requirements: 
         | 
| 68 | 
            +
              - - ">="
         | 
| 69 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 70 | 
            +
                  version: "0"
         | 
| 71 | 
            +
              version: 
         | 
| 72 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 73 | 
            +
              requirements: 
         | 
| 74 | 
            +
              - - ">="
         | 
| 75 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 76 | 
            +
                  version: "0"
         | 
| 77 | 
            +
              version: 
         | 
| 78 | 
            +
            requirements: []
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            rubyforge_project: 
         | 
| 81 | 
            +
            rubygems_version: 1.3.5
         | 
| 82 | 
            +
            signing_key: 
         | 
| 83 | 
            +
            specification_version: 3
         | 
| 84 | 
            +
            summary: Google Maps tile browser-downloader and manager
         | 
| 85 | 
            +
            test_files: []
         | 
| 86 | 
            +
             |