Index: AbstractLexicalAnalyzer.java
===================================================================
--- AbstractLexicalAnalyzer.java	(revision 44420)
+++ AbstractLexicalAnalyzer.java	(revision 44421)
@@ -313,6 +313,194 @@
         token.set(Token.STRING_LITERAL, buffer.toString());
     }
 
+    protected void parseSpecialNumber() {
+        char ch = getch();
+        if( ch!='@' ) {
+            throw new ExpressionSyntaxException(I18N.Wrong_special_number_start(), this);
+        }
+        buffer.clear();
+        ch = getch();
+        while (true) {
+            if (ch == EOF) {
+                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);
+            }
+            if( !Character.isAlphabetic(ch) ) {
+                break;
+            }
+            buffer.add(ch);
+            ch = getch();
+        }
+        String formatName = buffer.toString();
+        if( StringUtils.isBlank(formatName) ) {
+            formatName = "DMS";
+        }
+        formatName = formatName.toUpperCase();
+        /*
+        From https://es.wikipedia.org/wiki/Sistema_de_coordenadas#Coordenadas_geogr%C3%A1ficas
+        
+        DD --- Decimal Degree (Grados Polares)
+        DM --- Degree:Minute (Grados:Minutos)
+        DMS -- Degree:Minute:Second 
+        */
+        switch(formatName) {
+            case "DMS":
+                ungetch();
+                parseDMSNumber();
+                break;
+            default:
+                throw new ExpressionSyntaxException(I18N.Special_number_format_not_supported(formatName), this);
+        }
+    }
+
+    private void parseDMSNumber() {
+        int d;
+        int m;
+        int s_i;
+        int s_d;
+        double s;
+        String ll;
+        char ch;
+        
+        // Parseamos los grados
+        skipblanks();
+        ch = getch();
+        buffer.clear();
+        if( !Character.isDigit(ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
+        }
+        while (true) {
+            if (ch == EOF) {
+                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
+            }
+            if( !Character.isDigit(ch) ) {
+                break;
+            }
+            buffer.add(ch);
+            ch = getch();
+        }
+        if( !StringUtils.contains(" º:", ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
+        }
+        d = Integer.parseInt(buffer.toString());
+        
+        // Parseamos los minutos
+        skipblanks();
+        ch = getch();
+        buffer.clear();
+        if( !Character.isDigit(ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
+        }
+        while (true) {
+            if (ch == EOF) {
+                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
+            }
+            if( !Character.isDigit(ch) ) {
+                break;
+            }
+            buffer.add(ch);
+            ch = getch();
+        }
+        if( !StringUtils.contains(" ':", ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
+        }
+        m = Integer.parseInt(buffer.toString());
+
+        // Parseamos la parte entera de los segundos
+        skipblanks();
+        ch = getch();
+        buffer.clear();
+        if( !Character.isDigit(ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
+        }
+        while (true) {
+            if (ch == EOF) {
+                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
+            }
+            if( !Character.isDigit(ch) ) {
+                break;
+            }
+            buffer.add(ch);
+            ch = getch();
+        }
+        s_i = Integer.parseInt(buffer.toString());
+
+        if( ch == '.' ) {
+            // Parseamos la parte decimal de los segundos
+            skipblanks();
+            ch = getch();
+            buffer.clear();
+            if( !Character.isDigit(ch) ) {
+                throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
+            }
+            while (true) {
+                if (ch == EOF) {
+                    throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
+                }
+                if( !Character.isDigit(ch) ) {
+                    break;
+                }
+                buffer.add(ch);
+                ch = getch();
+            }            
+            s_d = Integer.parseInt(buffer.toString());
+        } else {
+            s_d = 0;
+        }
+        if( !StringUtils.contains(" \"", ch) ) {
+            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
+        }
+        
+        s = Double.parseDouble(s_i+"."+s_d);
+        
+        double dd = d + m / 60.0 + s / 3600.0;
+        
+        skipblanks();
+        ch = getch();
+        switch(ch) {
+            case 'N':
+            case 'n':
+                if( dd>90 ) {
+                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
+                }
+                dd = -dd;
+                break;
+                
+            case 'S':
+            case 's':
+                if( dd>90 ) {
+                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
+                }
+                break;
+            case 'E':
+            case 'e':
+                if( dd>180 ) {
+                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
+                }
+                break;
+                
+            case 'O':
+            case 'o':
+            case 'W':
+            case 'w':
+                if( dd>180 ) {
+                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
+                }
+                dd = -dd;
+                break;
+                
+            default:
+                throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX("N/S/E/W", String.valueOf(ch)), this);
+        }
+        ll = String.valueOf(Character.toUpperCase(ch));
+        
+        token.set(
+                Token.FLOATING_POINT_LITERAL,
+                String.format("@%dº %d' %f\" %s", d,m,s,ll) ,
+                dd
+        );
+        
+    }
+    
     protected void parseNumber() {
         this.nfPos.setIndex(this.position);
         Number n = nf.parse(source, this.nfPos);
