spatial_features 2.17.3 → 2.20.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.
- checksums.yaml +4 -4
- data/README.md +10 -1
- data/app/models/abstract_feature.rb +5 -2
- data/app/models/aggregate_feature.rb +5 -2
- data/app/models/feature.rb +6 -2
- data/lib/spatial_features/has_spatial_features/feature_import.rb +19 -2
- data/lib/spatial_features/has_spatial_features/queued_spatial_processing.rb +60 -4
- data/lib/spatial_features/importers/base.rb +2 -1
- data/lib/spatial_features/importers/kml.rb +30 -3
- data/lib/spatial_features/importers/kml_file.rb +7 -3
- data/lib/spatial_features/unzip.rb +4 -0
- data/lib/spatial_features/version.rb +1 -1
- metadata +6 -21
- data/config/initializers/chroma_serializers.rb +0 -15
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 639dee46fedd2cf507642b41119ff99e93506b54d891ab42d21a1f24fcf5a679
         | 
| 4 | 
            +
              data.tar.gz: 956175227c6a59acc867810a51796303218eac70c9bc49fe0d0f8222ea8558e6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6a1ac7a0dca15400e0e03f0f1c2192e22a4c1e818edde2a832f925e6ee803a9284c5236684228e25dbeabf2d147aacef4f4c05170216b0857941c87ed9b9a395
         | 
| 7 | 
            +
              data.tar.gz: da6fc8be7449202dfe46de18f0829e49664626ba17b90b6e3c1eae7a059f38a10d8310046e4f2bf44fb80bf78f2cdeea26624f429fc0d7b5873775b332086712
         | 
    
        data/README.md
    CHANGED
    
    | @@ -109,8 +109,17 @@ Person.new(:features => [Feature.new(:geog => 'some binary PostGIS Geography str | |
| 109 109 | 
             
            You can specify multiple import sources for geometry. Each key is a method that returns the data for the Importer, and
         | 
| 110 110 | 
             
            each value is the Importer to use to parse the data. See each Importer for more details.
         | 
| 111 111 | 
             
            ```ruby
         | 
| 112 | 
            +
            def ImageImporter
         | 
| 113 | 
            +
              def self.call(feature, image_paths)
         | 
| 114 | 
            +
                image_paths.each do |pathname|
         | 
| 115 | 
            +
                  # ...
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
            end
         | 
| 119 | 
            +
             | 
| 112 120 | 
             
            class Location < ActiveRecord::Base
         | 
| 113 | 
            -
              has_spatial_features :import => { :remote_kml_url => 'KMLFile', :file => 'File', :geojson => 'GeoJSON' }
         | 
| 121 | 
            +
              has_spatial_features :import => { :remote_kml_url => 'KMLFile', :file => 'File', :geojson => 'GeoJSON' },
         | 
| 122 | 
            +
                                   :image_handlers => ['ImageImporter']
         | 
| 114 123 |  | 
| 115 124 | 
             
              def remote_kml_url
         | 
| 116 125 | 
             
                "www.test.com/kml/#{id}.kml"
         | 
| @@ -49,8 +49,7 @@ class AbstractFeature < ActiveRecord::Base | |
| 49 49 | 
             
                where(:feature_type => 'point')
         | 
| 50 50 | 
             
              end
         | 
| 51 51 |  | 
| 52 | 
            -
              def self. | 
| 53 | 
            -
                # where("ST_DWithin(features.geog, ST_SetSRID( ST_Point( -71.104, 42.315), 4326)::geography, :distance)", :lat => lat, :lng => lng, :distance => distance_in_meters)
         | 
| 52 | 
            +
              def self.within_distance_of_point(lat, lng, distance_in_meters)
         | 
| 54 53 | 
             
                where("ST_DWithin(features.geog, ST_Point(:lng, :lat), :distance)", :lat => lat, :lng => lng, :distance => distance_in_meters)
         | 
| 55 54 | 
             
              end
         | 
| 56 55 |  | 
| @@ -73,6 +72,10 @@ class AbstractFeature < ActiveRecord::Base | |
| 73 72 | 
             
                join_other_features(other).where('ST_Intersects(features.geom_lowres, other_features.geom_lowres)').uniq
         | 
| 74 73 | 
             
              end
         | 
| 75 74 |  | 
| 75 | 
            +
              def self.within_distance(other, distance_in_meters)
         | 
| 76 | 
            +
                join_other_features(other).where('ST_DWithin(features.geom_lowres, other_features.geom_lowres, ?)', distance_in_meters).uniq
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 76 79 | 
             
              def self.invalid(column = 'geog::geometry')
         | 
| 77 80 | 
             
                select("features.*, ST_IsValidReason(#{column}) AS invalid_geometry_message").where.not("ST_IsValid(#{column})")
         | 
| 78 81 | 
             
              end
         | 
| @@ -4,7 +4,11 @@ class AggregateFeature < AbstractFeature | |
| 4 4 | 
             
              has_many :features, lambda { |aggregate| where(:spatial_model_type => aggregate.spatial_model_type) }, :foreign_key => :spatial_model_id, :primary_key => :spatial_model_id
         | 
| 5 5 |  | 
| 6 6 | 
             
              # Aggregate the features for the spatial model into a single feature
         | 
| 7 | 
            -
               | 
| 7 | 
            +
              before_validation :set_geog, :on => :create, :unless => :geog?
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              private
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def set_geog
         | 
| 8 12 | 
             
                feature_array_sql = <<~SQL
         | 
| 9 13 | 
             
                  ARRAY[
         | 
| 10 14 | 
             
                    (#{features.select('ST_UNION(ST_CollectionExtract(geog::geometry, 1))').to_sql}),
         | 
| @@ -19,6 +23,5 @@ class AggregateFeature < AbstractFeature | |
| 19 23 | 
             
                  FROM (SELECT unnest(#{feature_array_sql})) AS features
         | 
| 20 24 | 
             
                  WHERE NOT ST_IsEmpty(unnest)
         | 
| 21 25 | 
             
                SQL
         | 
| 22 | 
            -
                self.save!
         | 
| 23 26 | 
             
              end
         | 
| 24 27 | 
             
            end
         | 
    
        data/app/models/feature.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require_dependency SpatialFeatures::Engine.root.join('app/models/abstract_feature')
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class Feature < AbstractFeature
         | 
| 2 4 | 
             
              class_attribute :automatically_refresh_aggregate
         | 
| 3 5 | 
             
              self.automatically_refresh_aggregate = true
         | 
| @@ -13,6 +15,8 @@ class Feature < AbstractFeature | |
| 13 15 |  | 
| 14 16 | 
             
              after_save :refresh_aggregate, if: :automatically_refresh_aggregate?
         | 
| 15 17 |  | 
| 18 | 
            +
              attr_accessor :importable_image_paths # :nodoc:
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
              # Features are used for display so we also cache their KML representation
         | 
| 17 21 | 
             
              def self.cache_derivatives(options = {})
         | 
| 18 22 | 
             
                super
         | 
| @@ -52,8 +56,8 @@ class Feature < AbstractFeature | |
| 52 56 | 
             
              end
         | 
| 53 57 |  | 
| 54 58 | 
             
              def refresh_aggregate
         | 
| 55 | 
            -
                 | 
| 56 | 
            -
                 | 
| 59 | 
            +
                aggregate_feature&.destroy # Destroy the existing aggregate feature to ensure its cache key changes when it is refreshed
         | 
| 60 | 
            +
                create_aggregate_feature!
         | 
| 57 61 | 
             
              end
         | 
| 58 62 |  | 
| 59 63 | 
             
              def automatically_refresh_aggregate?
         | 
| @@ -8,7 +8,7 @@ module SpatialFeatures | |
| 8 8 | 
             
                included do
         | 
| 9 9 | 
             
                  extend ActiveModel::Callbacks
         | 
| 10 10 | 
             
                  define_model_callbacks :update_features
         | 
| 11 | 
            -
                  spatial_features_options.reverse_merge!(:import => {}, spatial_cache: [])
         | 
| 11 | 
            +
                  spatial_features_options.reverse_merge!(:import => {}, :spatial_cache => [], :image_handlers => [])
         | 
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 14 | 
             
                module ClassMethods
         | 
| @@ -78,13 +78,30 @@ module SpatialFeatures | |
| 78 78 | 
             
                  "SpatialFeatures::Importers::#{importer_name}".constantize
         | 
| 79 79 | 
             
                end
         | 
| 80 80 |  | 
| 81 | 
            +
                def handle_images(feature)
         | 
| 82 | 
            +
                  return if feature.importable_image_paths.nil? || feature.importable_image_paths.empty?
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  Array(spatial_features_options[:image_handlers]).each do |image_handler|
         | 
| 85 | 
            +
                    image_handler_from_name(image_handler).call(feature, feature.importable_image_paths)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def image_handler_from_name(handler_name)
         | 
| 90 | 
            +
                  handler_name.to_s.constantize
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 81 93 | 
             
                def import_features(imports, skip_invalid)
         | 
| 82 94 | 
             
                  features.delete_all
         | 
| 83 95 | 
             
                  valid, invalid = Feature.defer_aggregate_refresh do
         | 
| 84 96 | 
             
                    Feature.without_caching_derivatives do
         | 
| 85 97 | 
             
                      imports.flat_map(&:features).partition do |feature|
         | 
| 86 98 | 
             
                        feature.spatial_model = self
         | 
| 87 | 
            -
                        feature.save
         | 
| 99 | 
            +
                        if feature.save
         | 
| 100 | 
            +
                          handle_images(feature)
         | 
| 101 | 
            +
                          true
         | 
| 102 | 
            +
                        else
         | 
| 103 | 
            +
                          false
         | 
| 104 | 
            +
                        end
         | 
| 88 105 | 
             
                      end
         | 
| 89 106 | 
             
                    end
         | 
| 90 107 | 
             
                  end
         | 
| @@ -11,7 +11,22 @@ module SpatialFeatures | |
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 13 | 
             
                def updating_features?
         | 
| 14 | 
            -
                   | 
| 14 | 
            +
                  case spatial_processing_status(:update_features!)
         | 
| 15 | 
            +
                  when :queued, :processing
         | 
| 16 | 
            +
                    true
         | 
| 17 | 
            +
                  else
         | 
| 18 | 
            +
                    false
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def updating_features_failed?
         | 
| 23 | 
            +
                  spatial_processing_status(:update_features!) == :failure
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def spatial_processing_status(method_name)
         | 
| 27 | 
            +
                  if has_attribute?(:spatial_processing_status_cache)
         | 
| 28 | 
            +
                    spatial_processing_status_cache[method_name.to_s]&.to_sym
         | 
| 29 | 
            +
                  end
         | 
| 15 30 | 
             
                end
         | 
| 16 31 |  | 
| 17 32 | 
             
                def feature_update_error
         | 
| @@ -27,17 +42,58 @@ module SpatialFeatures | |
| 27 42 | 
             
                end
         | 
| 28 43 |  | 
| 29 44 | 
             
                def spatial_processing_jobs(suffix = nil)
         | 
| 30 | 
            -
                  Delayed::Job.where( | 
| 45 | 
            +
                  Delayed::Job.where(:queue => "#{spatial_processing_queue_name}#{suffix}")
         | 
| 31 46 | 
             
                end
         | 
| 32 47 |  | 
| 33 48 | 
             
                private
         | 
| 34 49 |  | 
| 35 50 | 
             
                def queue_spatial_task(method_name, *args)
         | 
| 36 | 
            -
                   | 
| 51 | 
            +
                  Delayed::Job.enqueue SpatialProcessingJob.new(self, method_name, *args), :queue => spatial_processing_queue_name + method_name
         | 
| 37 52 | 
             
                end
         | 
| 38 53 |  | 
| 39 54 | 
             
                def spatial_processing_queue_name
         | 
| 40 | 
            -
                  "#{ | 
| 55 | 
            +
                  "#{model_name}/#{id}/"
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                # CLASSES
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                class SpatialProcessingJob
         | 
| 61 | 
            +
                  def initialize(record, method_name, *args)
         | 
| 62 | 
            +
                    @record = record
         | 
| 63 | 
            +
                    @method_name = method_name
         | 
| 64 | 
            +
                    @args = args
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def enqueue(job)
         | 
| 68 | 
            +
                    update_cached_status(:queued)
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def perform
         | 
| 72 | 
            +
                    update_cached_status(:processing)
         | 
| 73 | 
            +
                    @record.send(@method_name, *@args)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def success(job)
         | 
| 77 | 
            +
                    update_cached_status(:success)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def error(job, exception)
         | 
| 81 | 
            +
                    update_cached_status(:failure)
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  def failure(job)
         | 
| 85 | 
            +
                    update_cached_status(:failure)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  private
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def update_cached_status(state)
         | 
| 91 | 
            +
                    if @record.has_attribute?(:spatial_processing_status_cache)
         | 
| 92 | 
            +
                      cache = @record.spatial_processing_status_cache || {}
         | 
| 93 | 
            +
                      cache[@method_name] = state
         | 
| 94 | 
            +
                      @record.update_column(:spatial_processing_status_cache, cache)
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 41 97 | 
             
                end
         | 
| 42 98 | 
             
              end
         | 
| 43 99 | 
             
            end
         | 
| @@ -46,7 +46,8 @@ module SpatialFeatures | |
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 48 | 
             
                  def build_feature(record)
         | 
| 49 | 
            -
                     | 
| 49 | 
            +
                    importable_image_paths = record.importable_image_paths if record.respond_to?(:importable_image_paths)
         | 
| 50 | 
            +
                    Feature.new(:name => record.name, :metadata => record.metadata, :feature_type => record.feature_type, :geog => record.geog, :importable_image_paths => importable_image_paths, :make_valid => @make_valid)
         | 
| 50 51 | 
             
                  end
         | 
| 51 52 | 
             
                end
         | 
| 52 53 | 
             
              end
         | 
| @@ -3,6 +3,14 @@ require 'ostruct' | |
| 3 3 | 
             
            module SpatialFeatures
         | 
| 4 4 | 
             
              module Importers
         | 
| 5 5 | 
             
                class KML < Base
         | 
| 6 | 
            +
                  # <SimpleData name> keys that may contain <img> tags
         | 
| 7 | 
            +
                  IMAGE_METADATA_KEYS = %w[pdfmaps_photos].freeze
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def initialize(data, base_dir: nil, **args)
         | 
| 10 | 
            +
                    @base_dir = base_dir
         | 
| 11 | 
            +
                    super data, **args
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 6 14 | 
             
                  private
         | 
| 7 15 |  | 
| 8 16 | 
             
                  def each_record(&block)
         | 
| @@ -18,10 +26,11 @@ module SpatialFeatures | |
| 18 26 | 
             
                        next if blank_feature?(feature)
         | 
| 19 27 |  | 
| 20 28 | 
             
                        geog = geom_from_kml(feature)
         | 
| 21 | 
            -
             | 
| 22 29 | 
             
                        next if geog.blank?
         | 
| 23 30 |  | 
| 24 | 
            -
                         | 
| 31 | 
            +
                        importable_image_paths = images_from_metadata(metadata)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                        yield OpenStruct.new(:feature_type => sql_type, :geog => geog, :name => name, :metadata => metadata, :importable_image_paths => importable_image_paths)
         | 
| 25 34 | 
             
                      end
         | 
| 26 35 | 
             
                    end
         | 
| 27 36 | 
             
                  end
         | 
| @@ -32,17 +41,35 @@ module SpatialFeatures | |
| 32 41 |  | 
| 33 42 | 
             
                  def geom_from_kml(kml)
         | 
| 34 43 | 
             
                    geom = nil
         | 
| 44 | 
            +
                    conn = nil
         | 
| 35 45 |  | 
| 36 46 | 
             
                    # Do query in a new thread so we use a new connection (if the query fails it will poison the transaction of the current connection)
         | 
| 47 | 
            +
                    #
         | 
| 48 | 
            +
                    # We manually checkout a new connection since Rails re-uses DB connections across threads.
         | 
| 37 49 | 
             
                    Thread.new do
         | 
| 38 | 
            -
                       | 
| 50 | 
            +
                      conn = ActiveRecord::Base.connection_pool.checkout
         | 
| 51 | 
            +
                      geom = conn.select_value("SELECT ST_GeomFromKML(#{conn.quote(kml.to_s)})")
         | 
| 39 52 | 
             
                    rescue ActiveRecord::StatementInvalid => e # Discard Invalid KML features
         | 
| 40 53 | 
             
                      geom = nil
         | 
| 54 | 
            +
                    ensure
         | 
| 55 | 
            +
                      ActiveRecord::Base.connection_pool.checkin(conn) if conn
         | 
| 41 56 | 
             
                    end.join
         | 
| 42 57 |  | 
| 43 58 | 
             
                    return geom
         | 
| 44 59 | 
             
                  end
         | 
| 45 60 |  | 
| 61 | 
            +
                  def images_from_metadata(metadata)
         | 
| 62 | 
            +
                    IMAGE_METADATA_KEYS.flat_map do |key|
         | 
| 63 | 
            +
                      images = metadata.delete(key)
         | 
| 64 | 
            +
                      next unless images
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      Nokogiri::HTML.fragment(images).css("img").map do |img|
         | 
| 67 | 
            +
                        next unless (src = img["src"])
         | 
| 68 | 
            +
                        @base_dir.join(src.downcase)
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                    end.compact
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 46 73 | 
             
                  def extract_metadata(placemark)
         | 
| 47 74 | 
             
                    metadata = {}
         | 
| 48 75 | 
             
                    metadata.merge! extract_table(placemark)
         | 
| @@ -1,13 +1,17 @@ | |
| 1 1 | 
             
            module SpatialFeatures
         | 
| 2 2 | 
             
              module Importers
         | 
| 3 3 | 
             
                class KMLFile < KML
         | 
| 4 | 
            -
                  def initialize(path_or_url,  | 
| 5 | 
            -
                     | 
| 6 | 
            -
             | 
| 4 | 
            +
                  def initialize(path_or_url, **options)
         | 
| 5 | 
            +
                    path = Download.open_each(path_or_url, unzip: [/\.kml$/], downcase: true).first
         | 
| 6 | 
            +
                    super ::File.read(path), base_dir: Pathname.new(path).dirname, **options
         | 
| 7 7 | 
             
                  rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError
         | 
| 8 8 | 
             
                    url = URI(path_or_url)
         | 
| 9 9 | 
             
                    raise ImportError, "KML server is not responding. Ensure server is running and accessible at #{[url.scheme, "//#{url.host}", url.port].select(&:present?).join(':')}."
         | 
| 10 10 | 
             
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def cache_key
         | 
| 13 | 
            +
                    @cache_key ||= Digest::MD5.hexdigest(@data)
         | 
| 14 | 
            +
                  end
         | 
| 11 15 | 
             
                end
         | 
| 12 16 | 
             
              end
         | 
| 13 17 | 
             
            end
         | 
| @@ -2,6 +2,9 @@ require 'fileutils' | |
| 2 2 |  | 
| 3 3 | 
             
            module SpatialFeatures
         | 
| 4 4 | 
             
              module Unzip
         | 
| 5 | 
            +
                # paths containing '__macosx' or beginning with a '.'
         | 
| 6 | 
            +
                IGNORED_ENTRY_PATHS = /(\A|\/)(__macosx|\.)/i.freeze
         | 
| 7 | 
            +
             | 
| 5 8 | 
             
                def self.paths(file_path, find: nil, **extract_options)
         | 
| 6 9 | 
             
                  paths = extract(file_path, **extract_options)
         | 
| 7 10 |  | 
| @@ -16,6 +19,7 @@ module SpatialFeatures | |
| 16 19 | 
             
                def self.extract(file_path, output_dir = Dir.mktmpdir, downcase: false)
         | 
| 17 20 | 
             
                  [].tap do |paths|
         | 
| 18 21 | 
             
                    entries(file_path).each do |entry|
         | 
| 22 | 
            +
                      next if entry.name =~ IGNORED_ENTRY_PATHS
         | 
| 19 23 | 
             
                      output_filename = entry.name
         | 
| 20 24 | 
             
                      output_filename = output_filename.downcase if downcase
         | 
| 21 25 | 
             
                      path = "#{output_dir}/#{output_filename}"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: spatial_features
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.20.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Ryan Wallace
         | 
| 8 8 | 
             
            - Nicholas Jakobsen
         | 
| 9 | 
            -
            autorequire:
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2021- | 
| 12 | 
            +
            date: 2021-12-27 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: rails
         | 
| @@ -87,20 +87,6 @@ dependencies: | |
| 87 87 | 
             
                - - "~>"
         | 
| 88 88 | 
             
                  - !ruby/object:Gem::Version
         | 
| 89 89 | 
             
                    version: '1.6'
         | 
| 90 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 91 | 
            -
              name: chroma
         | 
| 92 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 93 | 
            -
                requirements:
         | 
| 94 | 
            -
                - - "~>"
         | 
| 95 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            -
                    version: 0.1.0
         | 
| 97 | 
            -
              type: :runtime
         | 
| 98 | 
            -
              prerelease: false
         | 
| 99 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 100 | 
            -
                requirements:
         | 
| 101 | 
            -
                - - "~>"
         | 
| 102 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            -
                    version: 0.1.0
         | 
| 104 90 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 105 91 | 
             
              name: pg
         | 
| 106 92 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -144,7 +130,6 @@ files: | |
| 144 130 | 
             
            - app/models/feature.rb
         | 
| 145 131 | 
             
            - app/models/spatial_cache.rb
         | 
| 146 132 | 
             
            - app/models/spatial_proximity.rb
         | 
| 147 | 
            -
            - config/initializers/chroma_serializers.rb
         | 
| 148 133 | 
             
            - config/initializers/mime_types.rb
         | 
| 149 134 | 
             
            - config/initializers/register_oids.rb
         | 
| 150 135 | 
             
            - lib/spatial_features.rb
         | 
| @@ -175,7 +160,7 @@ homepage: https://github.com/culturecode/spatial_features | |
| 175 160 | 
             
            licenses:
         | 
| 176 161 | 
             
            - MIT
         | 
| 177 162 | 
             
            metadata: {}
         | 
| 178 | 
            -
            post_install_message:
         | 
| 163 | 
            +
            post_install_message: 
         | 
| 179 164 | 
             
            rdoc_options: []
         | 
| 180 165 | 
             
            require_paths:
         | 
| 181 166 | 
             
            - lib
         | 
| @@ -190,8 +175,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 190 175 | 
             
                - !ruby/object:Gem::Version
         | 
| 191 176 | 
             
                  version: '0'
         | 
| 192 177 | 
             
            requirements: []
         | 
| 193 | 
            -
            rubygems_version: 3.0. | 
| 194 | 
            -
            signing_key:
         | 
| 178 | 
            +
            rubygems_version: 3.0.3
         | 
| 179 | 
            +
            signing_key: 
         | 
| 195 180 | 
             
            specification_version: 4
         | 
| 196 181 | 
             
            summary: Adds spatial methods to a model.
         | 
| 197 182 | 
             
            test_files: []
         | 
| @@ -1,15 +0,0 @@ | |
| 1 | 
            -
            module Chroma
         | 
| 2 | 
            -
              class Color
         | 
| 3 | 
            -
                module Serializers
         | 
| 4 | 
            -
                  # Google's Fusion Table colouring expects the alpha value in the last position, not the first
         | 
| 5 | 
            -
                  def to_ft_hex
         | 
| 6 | 
            -
                    [
         | 
| 7 | 
            -
                     to_2char_hex(@rgb.r),
         | 
| 8 | 
            -
                     to_2char_hex(@rgb.g),
         | 
| 9 | 
            -
                     to_2char_hex(@rgb.b),
         | 
| 10 | 
            -
                     to_2char_hex(alpha * 255)
         | 
| 11 | 
            -
                    ].join('')
         | 
| 12 | 
            -
                  end
         | 
| 13 | 
            -
                end
         | 
| 14 | 
            -
              end
         | 
| 15 | 
            -
            end
         |