diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/EncapsulatedOpenBISService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/EncapsulatedOpenBISService.java
index e64c889dacdbcd7db3638ecae36e1b382076ad07..5da73443427312030281020930e581a3e6ef4c48 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/EncapsulatedOpenBISService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/EncapsulatedOpenBISService.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.dss.generic.server;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -49,6 +50,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatastoreServiceDescriptions;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria;
@@ -349,6 +351,20 @@ public final class EncapsulatedOpenBISService implements IEncapsulatedOpenBISSer
         }
     }
 
+    synchronized public Collection<VocabularyTerm> listVocabularyTerms(String vocabularyCode)
+            throws UserFailureException
+    {
+        checkSessionToken();
+        try
+        {
+            return service.listVocabularyTerms(sessionToken, vocabularyCode);
+        } catch (InvalidSessionException ex)
+        {
+            authenticate();
+            return service.listVocabularyTerms(sessionToken, vocabularyCode);
+        }
+    }
+
     synchronized public SampleType getSampleType(String sampleTypeCode) throws UserFailureException
     {
         checkSessionToken();
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IEncapsulatedOpenBISService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IEncapsulatedOpenBISService.java
index 3e306a6ae928f16d3a2c358701d50c86d664c24f..2899ae5da66de61840757aa9709c965bb1e8e462 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IEncapsulatedOpenBISService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IEncapsulatedOpenBISService.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.dss.generic.shared;
 
+import java.util.Collection;
 import java.util.List;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -35,6 +36,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewProperty;
@@ -103,6 +105,12 @@ public interface IEncapsulatedOpenBISService
     public SampleIdentifier tryToGetSampleIdentifier(String samplePermID)
             throws UserFailureException;
 
+    /**
+     * Lists vocabulary terms.
+     */
+    public Collection<VocabularyTerm> listVocabularyTerms(String vocabularyCode)
+            throws UserFailureException;
+
     /**
      * Gets the experiment type with assigned property types for the specified experiment type code.
      */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
index 536c7a2fa910e1457bfd74e1cdb618e6ea5ab73d..82b56be6b8c0603d707fe963be700033939aef9b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.generic.server;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -68,6 +69,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyTypeWithVocabul
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SourceType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
@@ -731,4 +733,18 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET
         return tryGetDataSet(sessionToken, dataSetCode);
     }
 
+    public Collection<VocabularyTerm> listVocabularyTerms(String sessionToken, String vocabularyCode)
+            throws UserFailureException
+    {
+        checkSession(sessionToken);
+        VocabularyPE vocabularyOrNull =
+                getDAOFactory().getVocabularyDAO().tryFindVocabularyByCode(vocabularyCode);
+        if (vocabularyOrNull == null)
+        {
+            throw new UserFailureException(String.format("Vocabulary '%s' not found",
+                    vocabularyCode));
+        }
+        return VocabularyTermTranslator.translateTerms(vocabularyOrNull.getTerms());
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
index 145bd0bee874422f7848a8a16a708f54dfcaf2bf..4d75ae797eb4e215029d10db0a7268d737aee19c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.generic.server;
 
+import java.util.Collection;
 import java.util.List;
 
 import ch.systemsx.cisd.authentication.ISessionManager;
@@ -36,6 +37,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatastoreServiceDescriptions;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria;
@@ -298,5 +300,11 @@ public class ETLServiceLogger extends AbstractServerLogger implements IETLServic
         return null;
     }
 
+    public Collection<VocabularyTerm> listVocabularyTerms(String sessionToken, String vocabulary)
+            throws UserFailureException
+    {
+        logAccess(sessionToken, "listVocabularyTerms", "VOCABULARY(%s)", vocabulary);
+        return null;
+    }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
index bd50ba20130d60471ae2f2bef3afab7eaf656285..8a2aa0ae7cece7a290666d8fd4dff4f8a05f1c12 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.generic.shared;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.springframework.transaction.annotation.Transactional;
@@ -53,6 +54,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria;
@@ -134,6 +136,14 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     public ExperimentType getExperimentType(String sessionToken, String experimentTypeCode)
             throws UserFailureException;
 
+    /**
+     * Returns a list of terms belonging to given vocabulary.
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
+    public Collection<VocabularyTerm> listVocabularyTerms(String sessionToken, String vocabulary)
+            throws UserFailureException;
+
     /**
      * Returns the SampleType together with assigned property types for specified sample type code.
      */
@@ -442,6 +452,4 @@ public interface IETLLIMSService extends IServer, ISessionProvider
             @AuthorizationGuard(guardClass = DataSetCodePredicate.class) String dataSetCode)
             throws UserFailureException;
 
-
-
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractHCSImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractHCSImageFileExtractor.java
index c55f76046940b3df501b6673f2e533f8a30d1876..065d4096c3eaf6c9d6df2567fe4925aeee22e6f5 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractHCSImageFileExtractor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractHCSImageFileExtractor.java
@@ -35,7 +35,6 @@ import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.bds.hcs.WellGeometry;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.filesystem.FileOperations;
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
@@ -147,16 +146,6 @@ abstract public class AbstractHCSImageFileExtractor implements IHCSImageFileExtr
         }
     }
 
-    public static final String[] IMAGE_EXTENSIONS = new String[]
-        { "tif", "tiff", "jpg", "jpeg", "gif", "png" };
-
-    protected List<File> listImageFiles(final File directory)
-    {
-        return FileOperations.getInstance().listFiles(directory, IMAGE_EXTENSIONS, true);
-    }
-
-    // -------------------------------
-
     protected static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, AbstractHCSImageFileExtractor.class);
 
@@ -226,7 +215,7 @@ abstract public class AbstractHCSImageFileExtractor implements IHCSImageFileExtr
     {
         List<File> invalidFiles = new LinkedList<File>();
         List<AcquiredPlateImage> acquiredImages = new ArrayList<AcquiredPlateImage>();
-        List<File> imageFiles = listImageFiles(incomingDataSetDirectory);
+        List<File> imageFiles = ImageFileExtractorUtils.listImageFiles(incomingDataSetDirectory);
         for (final File imageFile : imageFiles)
         {
             InterruptedExceptionUnchecked.check();
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 73e6340b9c51cf6fa9de3f782f908953a3afb5a8..d7c7c48083205f0723512c51ca795d9aead6e480 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
@@ -32,7 +32,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.Color
 
 /**
  * Generic image extractor implementation. The images names should have an extension present in
- * {@link #IMAGE_EXTENSIONS} constant. Each image name should adhere to the schema:<br>
+ * {@link ImageFileExtractorUtils#IMAGE_EXTENSIONS} constant. Each image name should adhere to the schema:<br>
  * 
  * <pre>
  * &lt;any-text&gt;_&lt;plate-code&gt;_&lt;well-code&gt;_&lt;tile-code&gt;_&lt;channel-name&gt;.&lt;allowed-image-extension&gt;
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImageFileExtractorUtils.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImageFileExtractorUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ccb815e3da4a752360a1c685d97e9e68c7f8f1d
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImageFileExtractorUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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.io.File;
+import java.util.List;
+
+import ch.systemsx.cisd.common.filesystem.FileOperations;
+
+/**
+ * Utility class for image file operations.
+ * 
+ * @author Izabela Adamczyk
+ */
+public class ImageFileExtractorUtils
+{
+
+    public static final String[] IMAGE_EXTENSIONS = new String[]
+        { "tif", "tiff", "jpg", "jpeg", "gif", "png" };
+
+    public static List<File> listImageFiles(final File directory)
+    {
+        return FileOperations.getInstance().listFiles(directory, IMAGE_EXTENSIONS, true);
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/BZDataSetInfoExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/BZDataSetInfoExtractor.java
index 9f8695c07e5760ab481329b242e46ce5a07e63b3..ed1ced149cf3a0c423fb8e5ae180430c137579cf 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/BZDataSetInfoExtractor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/BZDataSetInfoExtractor.java
@@ -18,16 +18,19 @@ package ch.systemsx.cisd.openbis.dss.etl.biozentrum;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Properties;
 
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang.StringUtils;
 
+import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
 import ch.systemsx.cisd.etlserver.IDataSetInfoExtractor;
+import ch.systemsx.cisd.openbis.dss.etl.ImageFileExtractorUtils;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
@@ -49,7 +52,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConst
 
 /**
  * Data set info extractor dealing with BZ data. Creates experiments and plates if needed.
- *
+ * 
  * @author Izabela Adamczyk
  */
 public class BZDataSetInfoExtractor implements IDataSetInfoExtractor
@@ -59,8 +62,6 @@ public class BZDataSetInfoExtractor implements IDataSetInfoExtractor
 
     static final String PROJECT_CODE = "project-code";
 
-    static final String PLATE_GEOMETRY = "plate-geometry";
-
     private final Properties properties;
 
     public BZDataSetInfoExtractor(final Properties properties)
@@ -76,7 +77,6 @@ public class BZDataSetInfoExtractor implements IDataSetInfoExtractor
         DirectoryDatasetInfoExtractor tokens =
                 new DirectoryDatasetInfoExtractor(FilenameUtils.getBaseName(incomingDataSetPath
                         .getPath()));
-        String plateGeometry = PropertyUtils.getMandatoryProperty(properties, PLATE_GEOMETRY);
         String spaceCode = PropertyUtils.getMandatoryProperty(properties, SPACE_CODE);
         String projectCode = PropertyUtils.getMandatoryProperty(properties, PROJECT_CODE);
         String sampleCode = getSampleCode(tokens);
@@ -89,6 +89,25 @@ public class BZDataSetInfoExtractor implements IDataSetInfoExtractor
         Sample sampleOrNull = openbisService.tryGetSampleWithExperiment(sampleIdentifier);
         if (sampleOrNull == null)
         {
+            Collection<VocabularyTerm> terms =
+                    openbisService.listVocabularyTerms(ScreeningConstants.PLATE_GEOMETRY);
+            List<String> plateGeometries = new ArrayList<String>();
+            for (VocabularyTerm v : terms)
+            {
+                plateGeometries.add(v.getCode());
+            }
+            List<File> imageFiles = ImageFileExtractorUtils.listImageFiles(incomingDataSetPath);
+            List<Location> plateLocations = new ArrayList<Location>();
+            for (File imageFile : imageFiles)
+            {
+                String baseName = FilenameUtils.getBaseName(imageFile.getPath());
+                String plateLocationToken =
+                        HCSImageFileExtractor.extractFileInfo(baseName).getPlateLocationToken();
+                plateLocations.add(Location
+                        .tryCreateLocationFromTransposedMatrixCoordinate(plateLocationToken));
+            }
+            String plateGeometry =
+                    PlateGeometryOracle.figureGeometry(plateLocations, plateGeometries);
             registerSampleWithExperiment(openbisService, sampleIdentifier, experimentIdentifier,
                     plateGeometry);
             sampleOrNull = openbisService.tryGetSampleWithExperiment(sampleIdentifier);
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
index cd8d347b8766385255ae6e789fc3f6abd2151218..27ec8b574803af3c07a137f02dc4b02e3e3e6ddc 100644
--- 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
@@ -22,7 +22,6 @@ 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;
 
 /**
@@ -44,7 +43,6 @@ public class HCSImageFileExtractor extends ch.systemsx.cisd.openbis.dss.etl.HCSI
         return extractFileInfo(FilenameUtils.getBaseName(imageFile.getPath()));
     }
 
-    @Private
     static ImageFileInfo extractFileInfo(String text)
     {
         String[] namedParts = StringUtils.split(text, "_");
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/PlateGeometryOracle.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/PlateGeometryOracle.java
new file mode 100644
index 0000000000000000000000000000000000000000..3af0c0fbf01ea6334904fc0daedafc9c87afa3f7
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/biozentrum/PlateGeometryOracle.java
@@ -0,0 +1,97 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.systemsx.cisd.bds.hcs.Location;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.shared.basic.utils.StringUtils;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Geometry;
+
+/**
+ * Based on the list of available plate locations and geometries tries to figure the correct plate
+ * geometry.
+ * 
+ * @author Izabela Adamczyk
+ */
+public class PlateGeometryOracle
+{
+
+    public static String figureGeometry(List<Location> plateLocations, List<String> plateGeometries)
+    {
+        return getMatchingGeometry(getMaxLocation(plateLocations), plateGeometries);
+    }
+
+    private static Location getMaxLocation(List<Location> locations)
+    {
+        int maxX = -1;
+        int maxY = -1;
+        for (Location l : locations)
+        {
+            if (maxX < l.getX())
+            {
+                maxX = l.getX();
+            }
+            if (maxY < l.getY())
+            {
+                maxY = l.getY();
+            }
+        }
+        return new Location(maxX, maxY);
+    }
+
+    private static String getMatchingGeometry(Location location, List<String> plateGeometries)
+    {
+        Map<Geometry, String> map = new HashMap<Geometry, String>();
+        List<Geometry> geometries = new ArrayList<Geometry>();
+        for (String geometryString : plateGeometries)
+        {
+            Geometry geometry = Geometry.createFromPlateGeometryString(geometryString);
+            geometries.add(geometry);
+            map.put(geometry, geometryString);
+        }
+        Collections.sort(geometries, new Comparator<Geometry>()
+            {
+
+                public int compare(Geometry a, Geometry b)
+                {
+                    return a.getHeight() * a.getWidth() - b.getHeight() * b.getWidth();
+                }
+            });
+        for (Geometry g : geometries)
+        {
+            if (isEnough(g, location))
+            {
+                return map.get(g);
+            }
+        }
+        throw new UserFailureException(String.format(
+                "Matching geometry not found (max location: %s, geometries: %s)", location,
+                StringUtils.joinList(plateGeometries)));
+    }
+
+    static private boolean isEnough(Geometry geometry, Location location)
+    {
+        return geometry.getDimX() >= location.getX() && geometry.getDimY() >= location.getY();
+    }
+}