diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractor.java
index 07c5025a9f9d63c804d1b9ecee42b12e2c22221e..ce09eb2110363d619a19ee4fae86b30d0cf13211 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractor.java
@@ -46,6 +46,10 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.Color
  */
 public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
 {
+    private static final String TILE_MAPPING = "tile_mapping";
+
+    private final TileMapper tileMapperOrNull;
+
     private final List<String> channelNames;
 
     private final List<ColorComponent> channelColorComponentsOrNull;
@@ -59,6 +63,8 @@ public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
         this.channelColorComponentsOrNull = tryGetChannelComponents(properties);
         checkChannelsAndColorComponents();
         this.wellGeometry = getWellGeometry(properties);
+        this.tileMapperOrNull =
+                TileMapper.tryCreate(properties.getProperty(TILE_MAPPING), wellGeometry);
     }
 
     private void checkChannelsAndColorComponents()
@@ -85,14 +91,22 @@ public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
      * </p>
      */
     @Override
-    protected final Location tryGetWellLocation(final String wellLocation)
+    protected Location tryGetWellLocation(final String wellLocation)
     {
         try
         {
             int tileNumber = Integer.parseInt(wellLocation);
-            Location letterLoc = Location.tryCreateLocationFromPosition(tileNumber, wellGeometry);
-            // transpose rows with columns
-            return new Location(letterLoc.getY(), letterLoc.getX());
+
+            if (tileMapperOrNull != null)
+            {
+                tileMapperOrNull.tryGetLocation(tileNumber);
+            } else
+            {
+                Location letterLoc =
+                        Location.tryCreateLocationFromPosition(tileNumber, wellGeometry);
+                // transpose rows with columns
+                return new Location(letterLoc.getY(), letterLoc.getX());
+            }
         } catch (final NumberFormatException ex)
         {
             // Nothing to do here. Rest of the code can handle this.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/TileMapper.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/TileMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b48c4a4dc8a0bc90c8c4dbeda5e1af3e5896eb1
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/TileMapper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+
+import ch.systemsx.cisd.bds.hcs.Geometry;
+import ch.systemsx.cisd.bds.hcs.Location;
+
+/**
+ * Defines the mapping from tile position to well location. Rows in the mapping string provided to
+ * the constructor are separated by semicolon (";"), columns are separated by comma (","). Position
+ * marked as zero ("-1"), means that it should be empty.
+ * <p>
+ * Usage Example: <code>
+ * TileMapper mapper = new TileMapper("1,-1,3;4,5,-1",geometry); 
+ * Location loc = mapper.tryGetLocation(4);
+ * </code>
+ * </p>
+ * 
+ * @author Izabela Adamczyk
+ */
+class TileMapper
+{
+
+    private final Map<Integer, Location> map = new HashMap<Integer, Location>();
+
+    public static TileMapper tryCreate(String mappingString, Geometry geometry)
+    {
+        if (StringUtils.isBlank(mappingString))
+        {
+            return null;
+        } else
+        {
+            return new TileMapper(mappingString, geometry);
+        }
+    }
+
+    private TileMapper(String mappingString, Geometry geometry)
+    {
+        assert StringUtils.isNotBlank(mappingString);
+        assert geometry != null;
+
+        int max = geometry.getColumns() * geometry.getRows() - 1;
+        String[] rows = StringUtils.split(mappingString, ";");
+        if (rows == null || rows.length != geometry.getRows())
+        {
+            throw new IllegalArgumentException(String.format(
+                    "Mapping does not match geometry. Number of rows expected: %s, but was: %s",
+                    geometry.getRows(), rows));
+        }
+        for (int r = 0; r < rows.length; r++)
+        {
+            String[] columns = StringUtils.split(rows[r], ",");
+            if (columns == null || columns.length != geometry.getColumns())
+            {
+                throw new IllegalArgumentException(
+                        String
+                                .format(
+                                        "Mapping does not match geometry. Number of columns expected: %s, but was: %s",
+                                        geometry.getColumns(), columns));
+            }
+            for (int c = 0; c < columns.length; c++)
+            {
+                int value = Integer.parseInt(columns[c]);
+                if (value > max || value < -1)
+                {
+                    throw new IllegalArgumentException(String.format(
+                            "Tile value out of range. Allowed: [-1, %s], was: %s.", max, value));
+                }
+                if (value != -1)
+                {
+                    if (map.get(value) != null)
+                    {
+                        throw new IllegalArgumentException(String.format(
+                                "Tile mapping for '%s' defined more than once.", value));
+
+                    }
+                    map.put(value, new Location(c + 1, r + 1));
+                }
+            }
+        }
+    }
+
+    public Location tryGetLocation(int position)
+    {
+        return map.get(position);
+    }
+
+}
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf284adf3f7fb2d54e49d6124f0ff4c2007b444b
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl.biozentrum;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.StringUtils;
+
+import ch.rinn.restrictions.Private;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
+
+/**
+ * A <code>IHCSImageFileExtractor</code> implementation suitable for <i>BioZentrum</i>.
+ * <p>
+ * Accepted file names convention:
+ * </p>
+ * 
+ * @author Izabela Adamczyk
+ */
+public class HCSImageFileExtractor extends ch.systemsx.cisd.openbis.dss.etl.HCSImageFileExtractor
+{
+
+    public HCSImageFileExtractor(Properties properties)
+    {
+        super(properties);
+    }
+
+    @Override
+    protected final ImageFileInfo tryExtractImageInfo(File imageFile, SampleIdentifier datasetSample)
+    {
+        return extractFileInfo(FilenameUtils.getBaseName(imageFile.getPath()));
+    }
+
+    @Private
+    static ImageFileInfo extractFileInfo(String text)
+    {
+        String[] namedParts = StringUtils.split(text, "_");
+        final String plateLocationToken = StringUtils.split(namedParts[3], "-")[1];
+        final String wellLocationToken = StringUtils.split(namedParts[4], "-")[1];
+        final String timepointToken = StringUtils.split(namedParts[5], "-")[1];
+        final String channelToken = StringUtils.split(namedParts[6], "-")[1];
+        ImageFileInfo info = new ImageFileInfo();
+        info.setPlateLocationToken(plateLocationToken);
+        info.setWellLocationToken(wellLocationToken);
+        info.setChannelToken(channelToken);
+        info.setTimepointToken(timepointToken);
+        return info;
+    }
+
+}
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/TileMapperTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/TileMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b134ef1844351cb303f96bd7eaf711b7c483cf97
--- /dev/null
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/TileMapperTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.bds.hcs.Geometry;
+
+/**
+ * Test cases for {@link TileMapper}.
+ * 
+ * @author Izabela Adamczyk
+ */
+public class TileMapperTest extends AssertJUnit
+{
+
+    @Test
+    public void testCreateWithNoMappingString() throws Exception
+    {
+        assertNull(TileMapper.tryCreate(null, new Geometry(1, 1)));
+    }
+
+    @Test
+    public void testCreateWithNullGeometry() throws Exception
+    {
+        boolean exceptionThrown = false;
+        try
+        {
+            TileMapper.tryCreate("1", null);
+        } catch (AssertionError ex)
+        {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+    }
+
+    @Test
+    public void testCreateWithCorrectMappingValues() throws Exception
+    {
+        TileMapper mapper = TileMapper.tryCreate("0,1;2,3;4,5", new Geometry(3, 2));
+        assertEquals(1, mapper.tryGetLocation(0).getX());
+        assertEquals(1, mapper.tryGetLocation(0).getY());
+
+        assertEquals(1, mapper.tryGetLocation(2).getX());
+        assertEquals(2, mapper.tryGetLocation(2).getY());
+
+        assertEquals(1, mapper.tryGetLocation(4).getX());
+        assertEquals(3, mapper.tryGetLocation(4).getY());
+
+        assertEquals(2, mapper.tryGetLocation(1).getX());
+        assertEquals(1, mapper.tryGetLocation(1).getY());
+
+        assertEquals(2, mapper.tryGetLocation(3).getX());
+        assertEquals(2, mapper.tryGetLocation(3).getY());
+
+        assertEquals(2, mapper.tryGetLocation(5).getX());
+        assertEquals(3, mapper.tryGetLocation(5).getY());
+    }
+
+    @Test
+    public void testCreateWithCorrectMappingValuesAndZero() throws Exception
+    {
+        TileMapper mapper = TileMapper.tryCreate("0,-1;-1,3;4,-1", new Geometry(3, 2));
+        assertEquals(1, mapper.tryGetLocation(0).getX());
+        assertEquals(1, mapper.tryGetLocation(0).getY());
+
+        assertEquals(1, mapper.tryGetLocation(4).getX());
+        assertEquals(3, mapper.tryGetLocation(4).getY());
+
+        assertEquals(2, mapper.tryGetLocation(3).getX());
+        assertEquals(2, mapper.tryGetLocation(3).getY());
+
+        assertNull(mapper.tryGetLocation(1));
+        assertNull(mapper.tryGetLocation(2));
+        assertNull(mapper.tryGetLocation(5));
+    }
+
+    @Test
+    public void testCreateWithMisssingMappingValuesRow() throws Exception
+    {
+        boolean exceptionThrown = false;
+        try
+        {
+            TileMapper.tryCreate("1,2;3,4", new Geometry(3, 2));
+        } catch (IllegalArgumentException ex)
+        {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+    }
+
+    @Test
+    public void testCreateWithMisssingMappingValuesColumn() throws Exception
+    {
+        boolean exceptionThrown = false;
+        try
+        {
+            TileMapper.tryCreate("1;3;5", new Geometry(3, 2));
+        } catch (IllegalArgumentException ex)
+        {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+    }
+
+    @Test
+    public void testCreateWithTooManyMappingValuesRows() throws Exception
+    {
+        boolean exceptionThrown = false;
+        try
+        {
+            TileMapper.tryCreate("1,2;3,4;5,6;7,8", new Geometry(3, 2));
+        } catch (IllegalArgumentException ex)
+        {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+    }
+
+    @Test
+    public void testCreateWithTooManyMappingValuesColumns() throws Exception
+    {
+        boolean exceptionThrown = false;
+        try
+        {
+            TileMapper.tryCreate("1,2,3;4,5,6;7,8,9", new Geometry(3, 2));
+        } catch (IllegalArgumentException ex)
+        {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+    }
+
+}
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractorTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2dc6e560edd75d0e2fbd70e04adc6903211c9b4
--- /dev/null
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/HCSImageFileExtractorTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl.biozentrum;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.rinn.restrictions.Friend;
+import ch.systemsx.cisd.openbis.dss.etl.AbstractHCSImageFileExtractor.ImageFileInfo;
+
+/**
+ * Test cases for {@link HCSImageFileExtractor}.
+ * 
+ * @author Izabela Adamczyk
+ */
+@Friend(toClasses = HCSImageFileExtractor.class)
+public class HCSImageFileExtractorTest extends AssertJUnit
+{
+    @Test
+    public void testExtractFileInfoCorrectFileName() throws Exception
+    {
+        ImageFileInfo info =
+                HCSImageFileExtractor
+                        .extractFileInfo("SM100719invasomes_plt-1_bc-UNK_wp-A01_s-10_t-1_wl-Cy3_001");
+        assertEquals("plate location token", "A01", info.getPlateLocationToken());
+        assertEquals("channel token", "Cy3", info.getChannelToken());
+        assertEquals("time point token", "1", info.getTimepointToken());
+        assertEquals("well location token", "10", info.getWellLocationToken());
+    }
+}