Module: Map::Coordinate
- Defined in:
- app/models/map.rb
Constant Summary collapse
- PLATE_DIMENSIONS =
Note:
- 96 represents a 96-well plate, arranged in 12 columns and 8 rows.
- 384 represents a 384-well plate, arranged in 24 columns and 16 rows.
- 16 represents a 16-well Chromium Chip, which has 8 columns and 2 rows. Although a 16-well Chromium Chip does not have 3:2 ratio to be a standard plate, i.e. it has 4:1 ratio, the methods here still apply. Note that the asset shape is a generic one, Shape4x1, although it has been created for the 16-well Chromium Chip.
- 8 represents a 8-well Chromium Chip, which has 8 columns and 1 row. Note that the asset shape is a generic one, Shape8x1, although it has been created for the 8-well Chromium Chip.
A hash representing the dimensions of different types of plates. The keys are the total number of wells in the plate, and the values are arrays, where the first element is the number of columns and the second element is the number of rows.
Hash.new { |_h, _k| [] }.merge(96 => [12, 8], 384 => [24, 16], 16 => [8, 2], 8 => [8, 1])
Instance Attribute Summary collapse
-
#asset_size ⇒ Integer
The plate size for which the Map corresponds.
-
#column_order ⇒ Integer
Zero indexed order of the well when sorted by column.
-
#description ⇒ String
The name of the well.
-
#row_order ⇒ Integer
Zero indexed order of the well when sorted by row.
Class Method Summary collapse
-
.alternate_position(well_position, size, *dimensions) ⇒ Object
Given the well position described in terms of a direction (vertical or horizontal) this function will map it to the alternate positional representation, i.e.
-
.by_column_map_index_to_well_description(well_position, plate_size) ⇒ Object
e.g.
-
.by_row_map_index_to_well_description(well_position, plate_size) ⇒ Object
e.g.
- .description_to_horizontal_plate_position(well_description, plate_size) ⇒ Object
- .descriptions_for_column(column, size) ⇒ Object
- .descriptions_for_row(row, size) ⇒ Object
- .horizontal_position_to_description(well_position, width) ⇒ Object
- .horizontal_to_vertical(well_position, plate_size) ⇒ Object
- .location_from_index(index, size) ⇒ Object
-
.location_from_row_and_column(row, column, _ = nil, __ = nil) ⇒ Object
Seems to expect row to be zero-indexed but column to be 1 indexed.
- .plate_length(plate_size) ⇒ Object
- .plate_width(plate_size) ⇒ Object
-
.vertical_position_to_description(well_position, length) ⇒ Object
well number counting by columns, length is the number of rows in the plate e.g.
- .vertical_to_horizontal(well_position, plate_size) ⇒ Object
-
.well_description_to_by_column_map_index(well_description, plate_size) ⇒ Object
e.g.
Instance Attribute Details
#asset_size ⇒ Integer
Returns the plate size for which the Map corresponds.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/map.rb', line 17 module Coordinate # TODO: These methods are only valid for standard plates. Moved them here to make that more explicit # (even if its not strictly appropriate) They could do with refactoring/removing. # A hash representing the dimensions of different types of plates. # The keys are the total number of wells in the plate, and the values are # arrays, where the first element is the number of columns and the second # element is the number of rows. # # @note # - 96 represents a 96-well plate, arranged in 12 columns and 8 rows. # - 384 represents a 384-well plate, arranged in 24 columns and 16 rows. # - 16 represents a 16-well Chromium Chip, which has 8 columns and 2 rows. # Although a 16-well Chromium Chip does not have 3:2 ratio to be a # standard plate, i.e. it has 4:1 ratio, the methods here still apply. # Note that the asset shape is a generic one, Shape4x1, although it has # been created for the 16-well Chromium Chip. # - 8 represents a 8-well Chromium Chip, which has 8 columns and 1 row. # Note that the asset shape is a generic one, Shape8x1, although it has # been created for the 8-well Chromium Chip. # @return [Hash{Integer => Array<Integer>}] the dimensions of the plates PLATE_DIMENSIONS = Hash.new { |_h, _k| [] }.merge(96 => [12, 8], 384 => [24, 16], 16 => [8, 2], 8 => [8, 1]) # Seems to expect row to be zero-indexed but column to be 1 indexed def self.location_from_row_and_column(row, column, _ = nil, __ = nil) "#{('A'.getbyte(0) + row).chr}#{column}" end def self.description_to_horizontal_plate_position(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) width = plate_width(plate_size) return nil if width.nil? (width * split_well[:row]) + split_well[:col] end # e.g. B5 returns 34 for a 96 well plate # So 4 columns of 8 rows each, plus 2 for the B row def self.well_description_to_by_column_map_index(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) length = plate_length(plate_size) return nil if length.nil? (length * (split_well[:col] - 1)) + split_well[:row] + 1 end # e.g. 23 returns B11 for a 96 well plate # So 1 full row of 12, plus 11, to give row B, column 11 def self.by_row_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) width = plate_width(plate_size) return nil if width.nil? horizontal_position_to_description(well_position, width) end # e.g. 23 returns G3 for a 96 well plate # So 2 full columns of 8, plus 7, to give row G, column 3 def self.by_column_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) length = plate_length(plate_size) return nil if length.nil? vertical_position_to_description(well_position, length) end def self.descriptions_for_row(row, size) (1..plate_width(size)).map { |column| "#{row}#{column}" } end def self.descriptions_for_column(column, size) (0...plate_length(size)).map { |row| Map.location_from_row_and_column(row, column) } end def self.plate_width(plate_size) PLATE_DIMENSIONS[plate_size].first end def self.plate_length(plate_size) PLATE_DIMENSIONS[plate_size].last end # well number counting by columns, length is the number of rows in the plate # e.g. B5 sends this 34 and 8 def self.vertical_position_to_description(well_position, length) desc_letter = (((well_position - 1) % length) + 65).chr desc_number = ((well_position - 1) / length) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_position_to_description(well_position, width) desc_letter = (((well_position - 1) / width) + 65).chr desc_number = ((well_position - 1) % width) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_to_vertical(well_position, plate_size) alternate_position(well_position, plate_size, :width, :length) end def self.vertical_to_horizontal(well_position, plate_size) alternate_position(well_position, plate_size, :length, :width) end def self.location_from_index(index, size) by_row_map_index_to_well_description(index + 1, size) end class << self # Given the well position described in terms of a direction (vertical or horizontal) this function # will map it to the alternate positional representation, i.e. a vertical position will be mapped # to a horizontal one. It does this with the divisor and multiplier, which will be reversed for # the alternate. # # NOTE: I don't like this, it just makes things clearer than it was! # NOTE: I hate the nil returns but external code would take too long to change this time round def alternate_position(well_position, size, *dimensions) return nil unless Map.valid_well_position?(well_position) divisor, multiplier = dimensions.map { |n| send(:"plate_#{n}", size) } return nil if divisor.nil? || multiplier.nil? column, row = (well_position - 1).divmod(divisor) return nil unless (0...multiplier).cover?(column) return nil unless (0...divisor).cover?(row) (row * multiplier) + column + 1 end private :alternate_position end end |
#column_order ⇒ Integer
Returns zero indexed order of the well when sorted by column.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/map.rb', line 17 module Coordinate # TODO: These methods are only valid for standard plates. Moved them here to make that more explicit # (even if its not strictly appropriate) They could do with refactoring/removing. # A hash representing the dimensions of different types of plates. # The keys are the total number of wells in the plate, and the values are # arrays, where the first element is the number of columns and the second # element is the number of rows. # # @note # - 96 represents a 96-well plate, arranged in 12 columns and 8 rows. # - 384 represents a 384-well plate, arranged in 24 columns and 16 rows. # - 16 represents a 16-well Chromium Chip, which has 8 columns and 2 rows. # Although a 16-well Chromium Chip does not have 3:2 ratio to be a # standard plate, i.e. it has 4:1 ratio, the methods here still apply. # Note that the asset shape is a generic one, Shape4x1, although it has # been created for the 16-well Chromium Chip. # - 8 represents a 8-well Chromium Chip, which has 8 columns and 1 row. # Note that the asset shape is a generic one, Shape8x1, although it has # been created for the 8-well Chromium Chip. # @return [Hash{Integer => Array<Integer>}] the dimensions of the plates PLATE_DIMENSIONS = Hash.new { |_h, _k| [] }.merge(96 => [12, 8], 384 => [24, 16], 16 => [8, 2], 8 => [8, 1]) # Seems to expect row to be zero-indexed but column to be 1 indexed def self.location_from_row_and_column(row, column, _ = nil, __ = nil) "#{('A'.getbyte(0) + row).chr}#{column}" end def self.description_to_horizontal_plate_position(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) width = plate_width(plate_size) return nil if width.nil? (width * split_well[:row]) + split_well[:col] end # e.g. B5 returns 34 for a 96 well plate # So 4 columns of 8 rows each, plus 2 for the B row def self.well_description_to_by_column_map_index(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) length = plate_length(plate_size) return nil if length.nil? (length * (split_well[:col] - 1)) + split_well[:row] + 1 end # e.g. 23 returns B11 for a 96 well plate # So 1 full row of 12, plus 11, to give row B, column 11 def self.by_row_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) width = plate_width(plate_size) return nil if width.nil? horizontal_position_to_description(well_position, width) end # e.g. 23 returns G3 for a 96 well plate # So 2 full columns of 8, plus 7, to give row G, column 3 def self.by_column_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) length = plate_length(plate_size) return nil if length.nil? vertical_position_to_description(well_position, length) end def self.descriptions_for_row(row, size) (1..plate_width(size)).map { |column| "#{row}#{column}" } end def self.descriptions_for_column(column, size) (0...plate_length(size)).map { |row| Map.location_from_row_and_column(row, column) } end def self.plate_width(plate_size) PLATE_DIMENSIONS[plate_size].first end def self.plate_length(plate_size) PLATE_DIMENSIONS[plate_size].last end # well number counting by columns, length is the number of rows in the plate # e.g. B5 sends this 34 and 8 def self.vertical_position_to_description(well_position, length) desc_letter = (((well_position - 1) % length) + 65).chr desc_number = ((well_position - 1) / length) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_position_to_description(well_position, width) desc_letter = (((well_position - 1) / width) + 65).chr desc_number = ((well_position - 1) % width) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_to_vertical(well_position, plate_size) alternate_position(well_position, plate_size, :width, :length) end def self.vertical_to_horizontal(well_position, plate_size) alternate_position(well_position, plate_size, :length, :width) end def self.location_from_index(index, size) by_row_map_index_to_well_description(index + 1, size) end class << self # Given the well position described in terms of a direction (vertical or horizontal) this function # will map it to the alternate positional representation, i.e. a vertical position will be mapped # to a horizontal one. It does this with the divisor and multiplier, which will be reversed for # the alternate. # # NOTE: I don't like this, it just makes things clearer than it was! # NOTE: I hate the nil returns but external code would take too long to change this time round def alternate_position(well_position, size, *dimensions) return nil unless Map.valid_well_position?(well_position) divisor, multiplier = dimensions.map { |n| send(:"plate_#{n}", size) } return nil if divisor.nil? || multiplier.nil? column, row = (well_position - 1).divmod(divisor) return nil unless (0...multiplier).cover?(column) return nil unless (0...divisor).cover?(row) (row * multiplier) + column + 1 end private :alternate_position end end |
#description ⇒ String
Returns the name of the well. In most cases this will be in the along the lines of A1 or H12.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/map.rb', line 17 module Coordinate # TODO: These methods are only valid for standard plates. Moved them here to make that more explicit # (even if its not strictly appropriate) They could do with refactoring/removing. # A hash representing the dimensions of different types of plates. # The keys are the total number of wells in the plate, and the values are # arrays, where the first element is the number of columns and the second # element is the number of rows. # # @note # - 96 represents a 96-well plate, arranged in 12 columns and 8 rows. # - 384 represents a 384-well plate, arranged in 24 columns and 16 rows. # - 16 represents a 16-well Chromium Chip, which has 8 columns and 2 rows. # Although a 16-well Chromium Chip does not have 3:2 ratio to be a # standard plate, i.e. it has 4:1 ratio, the methods here still apply. # Note that the asset shape is a generic one, Shape4x1, although it has # been created for the 16-well Chromium Chip. # - 8 represents a 8-well Chromium Chip, which has 8 columns and 1 row. # Note that the asset shape is a generic one, Shape8x1, although it has # been created for the 8-well Chromium Chip. # @return [Hash{Integer => Array<Integer>}] the dimensions of the plates PLATE_DIMENSIONS = Hash.new { |_h, _k| [] }.merge(96 => [12, 8], 384 => [24, 16], 16 => [8, 2], 8 => [8, 1]) # Seems to expect row to be zero-indexed but column to be 1 indexed def self.location_from_row_and_column(row, column, _ = nil, __ = nil) "#{('A'.getbyte(0) + row).chr}#{column}" end def self.description_to_horizontal_plate_position(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) width = plate_width(plate_size) return nil if width.nil? (width * split_well[:row]) + split_well[:col] end # e.g. B5 returns 34 for a 96 well plate # So 4 columns of 8 rows each, plus 2 for the B row def self.well_description_to_by_column_map_index(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) length = plate_length(plate_size) return nil if length.nil? (length * (split_well[:col] - 1)) + split_well[:row] + 1 end # e.g. 23 returns B11 for a 96 well plate # So 1 full row of 12, plus 11, to give row B, column 11 def self.by_row_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) width = plate_width(plate_size) return nil if width.nil? horizontal_position_to_description(well_position, width) end # e.g. 23 returns G3 for a 96 well plate # So 2 full columns of 8, plus 7, to give row G, column 3 def self.by_column_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) length = plate_length(plate_size) return nil if length.nil? vertical_position_to_description(well_position, length) end def self.descriptions_for_row(row, size) (1..plate_width(size)).map { |column| "#{row}#{column}" } end def self.descriptions_for_column(column, size) (0...plate_length(size)).map { |row| Map.location_from_row_and_column(row, column) } end def self.plate_width(plate_size) PLATE_DIMENSIONS[plate_size].first end def self.plate_length(plate_size) PLATE_DIMENSIONS[plate_size].last end # well number counting by columns, length is the number of rows in the plate # e.g. B5 sends this 34 and 8 def self.vertical_position_to_description(well_position, length) desc_letter = (((well_position - 1) % length) + 65).chr desc_number = ((well_position - 1) / length) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_position_to_description(well_position, width) desc_letter = (((well_position - 1) / width) + 65).chr desc_number = ((well_position - 1) % width) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_to_vertical(well_position, plate_size) alternate_position(well_position, plate_size, :width, :length) end def self.vertical_to_horizontal(well_position, plate_size) alternate_position(well_position, plate_size, :length, :width) end def self.location_from_index(index, size) by_row_map_index_to_well_description(index + 1, size) end class << self # Given the well position described in terms of a direction (vertical or horizontal) this function # will map it to the alternate positional representation, i.e. a vertical position will be mapped # to a horizontal one. It does this with the divisor and multiplier, which will be reversed for # the alternate. # # NOTE: I don't like this, it just makes things clearer than it was! # NOTE: I hate the nil returns but external code would take too long to change this time round def alternate_position(well_position, size, *dimensions) return nil unless Map.valid_well_position?(well_position) divisor, multiplier = dimensions.map { |n| send(:"plate_#{n}", size) } return nil if divisor.nil? || multiplier.nil? column, row = (well_position - 1).divmod(divisor) return nil unless (0...multiplier).cover?(column) return nil unless (0...divisor).cover?(row) (row * multiplier) + column + 1 end private :alternate_position end end |
#row_order ⇒ Integer
Returns zero indexed order of the well when sorted by row.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/map.rb', line 17 module Coordinate # TODO: These methods are only valid for standard plates. Moved them here to make that more explicit # (even if its not strictly appropriate) They could do with refactoring/removing. # A hash representing the dimensions of different types of plates. # The keys are the total number of wells in the plate, and the values are # arrays, where the first element is the number of columns and the second # element is the number of rows. # # @note # - 96 represents a 96-well plate, arranged in 12 columns and 8 rows. # - 384 represents a 384-well plate, arranged in 24 columns and 16 rows. # - 16 represents a 16-well Chromium Chip, which has 8 columns and 2 rows. # Although a 16-well Chromium Chip does not have 3:2 ratio to be a # standard plate, i.e. it has 4:1 ratio, the methods here still apply. # Note that the asset shape is a generic one, Shape4x1, although it has # been created for the 16-well Chromium Chip. # - 8 represents a 8-well Chromium Chip, which has 8 columns and 1 row. # Note that the asset shape is a generic one, Shape8x1, although it has # been created for the 8-well Chromium Chip. # @return [Hash{Integer => Array<Integer>}] the dimensions of the plates PLATE_DIMENSIONS = Hash.new { |_h, _k| [] }.merge(96 => [12, 8], 384 => [24, 16], 16 => [8, 2], 8 => [8, 1]) # Seems to expect row to be zero-indexed but column to be 1 indexed def self.location_from_row_and_column(row, column, _ = nil, __ = nil) "#{('A'.getbyte(0) + row).chr}#{column}" end def self.description_to_horizontal_plate_position(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) width = plate_width(plate_size) return nil if width.nil? (width * split_well[:row]) + split_well[:col] end # e.g. B5 returns 34 for a 96 well plate # So 4 columns of 8 rows each, plus 2 for the B row def self.well_description_to_by_column_map_index(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) length = plate_length(plate_size) return nil if length.nil? (length * (split_well[:col] - 1)) + split_well[:row] + 1 end # e.g. 23 returns B11 for a 96 well plate # So 1 full row of 12, plus 11, to give row B, column 11 def self.by_row_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) width = plate_width(plate_size) return nil if width.nil? horizontal_position_to_description(well_position, width) end # e.g. 23 returns G3 for a 96 well plate # So 2 full columns of 8, plus 7, to give row G, column 3 def self.by_column_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) length = plate_length(plate_size) return nil if length.nil? vertical_position_to_description(well_position, length) end def self.descriptions_for_row(row, size) (1..plate_width(size)).map { |column| "#{row}#{column}" } end def self.descriptions_for_column(column, size) (0...plate_length(size)).map { |row| Map.location_from_row_and_column(row, column) } end def self.plate_width(plate_size) PLATE_DIMENSIONS[plate_size].first end def self.plate_length(plate_size) PLATE_DIMENSIONS[plate_size].last end # well number counting by columns, length is the number of rows in the plate # e.g. B5 sends this 34 and 8 def self.vertical_position_to_description(well_position, length) desc_letter = (((well_position - 1) % length) + 65).chr desc_number = ((well_position - 1) / length) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_position_to_description(well_position, width) desc_letter = (((well_position - 1) / width) + 65).chr desc_number = ((well_position - 1) % width) + 1 (desc_letter + desc_number.to_s) end def self.horizontal_to_vertical(well_position, plate_size) alternate_position(well_position, plate_size, :width, :length) end def self.vertical_to_horizontal(well_position, plate_size) alternate_position(well_position, plate_size, :length, :width) end def self.location_from_index(index, size) by_row_map_index_to_well_description(index + 1, size) end class << self # Given the well position described in terms of a direction (vertical or horizontal) this function # will map it to the alternate positional representation, i.e. a vertical position will be mapped # to a horizontal one. It does this with the divisor and multiplier, which will be reversed for # the alternate. # # NOTE: I don't like this, it just makes things clearer than it was! # NOTE: I hate the nil returns but external code would take too long to change this time round def alternate_position(well_position, size, *dimensions) return nil unless Map.valid_well_position?(well_position) divisor, multiplier = dimensions.map { |n| send(:"plate_#{n}", size) } return nil if divisor.nil? || multiplier.nil? column, row = (well_position - 1).divmod(divisor) return nil unless (0...multiplier).cover?(column) return nil unless (0...divisor).cover?(row) (row * multiplier) + column + 1 end private :alternate_position end end |
Class Method Details
.alternate_position(well_position, size, *dimensions) ⇒ Object
Given the well position described in terms of a direction (vertical or horizontal) this function will map it to the alternate positional representation, i.e. a vertical position will be mapped to a horizontal one. It does this with the divisor and multiplier, which will be reversed for the alternate.
NOTE: I don't like this, it just makes things clearer than it was! NOTE: I hate the nil returns but external code would take too long to change this time round
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'app/models/map.rb', line 139 def alternate_position(well_position, size, *dimensions) return nil unless Map.valid_well_position?(well_position) divisor, multiplier = dimensions.map { |n| send(:"plate_#{n}", size) } return nil if divisor.nil? || multiplier.nil? column, row = (well_position - 1).divmod(divisor) return nil unless (0...multiplier).cover?(column) return nil unless (0...divisor).cover?(row) (row * multiplier) + column + 1 end |
.by_column_map_index_to_well_description(well_position, plate_size) ⇒ Object
e.g. 23 returns G3 for a 96 well plate So 2 full columns of 8, plus 7, to give row G, column 3
80 81 82 83 84 85 86 87 |
# File 'app/models/map.rb', line 80 def self.by_column_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) length = plate_length(plate_size) return nil if length.nil? vertical_position_to_description(well_position, length) end |
.by_row_map_index_to_well_description(well_position, plate_size) ⇒ Object
e.g. 23 returns B11 for a 96 well plate So 1 full row of 12, plus 11, to give row B, column 11
69 70 71 72 73 74 75 76 |
# File 'app/models/map.rb', line 69 def self.by_row_map_index_to_well_description(well_position, plate_size) return nil unless Map.valid_plate_position_and_plate_size?(well_position, plate_size) width = plate_width(plate_size) return nil if width.nil? horizontal_position_to_description(well_position, width) end |
.description_to_horizontal_plate_position(well_description, plate_size) ⇒ Object
45 46 47 48 49 50 51 52 53 |
# File 'app/models/map.rb', line 45 def self.description_to_horizontal_plate_position(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) width = plate_width(plate_size) return nil if width.nil? (width * split_well[:row]) + split_well[:col] end |
.descriptions_for_column(column, size) ⇒ Object
93 94 95 |
# File 'app/models/map.rb', line 93 def self.descriptions_for_column(column, size) (0...plate_length(size)).map { |row| Map.location_from_row_and_column(row, column) } end |
.descriptions_for_row(row, size) ⇒ Object
89 90 91 |
# File 'app/models/map.rb', line 89 def self.descriptions_for_row(row, size) (1..plate_width(size)).map { |column| "#{row}#{column}" } end |
.horizontal_position_to_description(well_position, width) ⇒ Object
113 114 115 116 117 |
# File 'app/models/map.rb', line 113 def self.horizontal_position_to_description(well_position, width) desc_letter = (((well_position - 1) / width) + 65).chr desc_number = ((well_position - 1) % width) + 1 (desc_letter + desc_number.to_s) end |
.horizontal_to_vertical(well_position, plate_size) ⇒ Object
119 120 121 |
# File 'app/models/map.rb', line 119 def self.horizontal_to_vertical(well_position, plate_size) alternate_position(well_position, plate_size, :width, :length) end |
.location_from_index(index, size) ⇒ Object
127 128 129 |
# File 'app/models/map.rb', line 127 def self.location_from_index(index, size) by_row_map_index_to_well_description(index + 1, size) end |
.location_from_row_and_column(row, column, _ = nil, __ = nil) ⇒ Object
Seems to expect row to be zero-indexed but column to be 1 indexed
41 42 43 |
# File 'app/models/map.rb', line 41 def self.location_from_row_and_column(row, column, _ = nil, __ = nil) "#{('A'.getbyte(0) + row).chr}#{column}" end |
.plate_length(plate_size) ⇒ Object
101 102 103 |
# File 'app/models/map.rb', line 101 def self.plate_length(plate_size) PLATE_DIMENSIONS[plate_size].last end |
.plate_width(plate_size) ⇒ Object
97 98 99 |
# File 'app/models/map.rb', line 97 def self.plate_width(plate_size) PLATE_DIMENSIONS[plate_size].first end |
.vertical_position_to_description(well_position, length) ⇒ Object
well number counting by columns, length is the number of rows in the plate e.g. B5 sends this 34 and 8
107 108 109 110 111 |
# File 'app/models/map.rb', line 107 def self.vertical_position_to_description(well_position, length) desc_letter = (((well_position - 1) % length) + 65).chr desc_number = ((well_position - 1) / length) + 1 (desc_letter + desc_number.to_s) end |
.vertical_to_horizontal(well_position, plate_size) ⇒ Object
123 124 125 |
# File 'app/models/map.rb', line 123 def self.vertical_to_horizontal(well_position, plate_size) alternate_position(well_position, plate_size, :length, :width) end |
.well_description_to_by_column_map_index(well_description, plate_size) ⇒ Object
e.g. B5 returns 34 for a 96 well plate So 4 columns of 8 rows each, plus 2 for the B row
57 58 59 60 61 62 63 64 65 |
# File 'app/models/map.rb', line 57 def self.well_description_to_by_column_map_index(well_description, plate_size) return nil unless Map.valid_well_description_and_plate_size?(well_description, plate_size) split_well = Map.split_well_description(well_description) length = plate_length(plate_size) return nil if length.nil? (length * (split_well[:col] - 1)) + split_well[:row] + 1 end |