OpenLayers OpenLayers

Ticket #915: geojson.patch

File geojson.patch, 57.2 kB (added by crschmidt, 1 year ago)

patch against trunk for geojson format

  • tests/Format/test_GeoJSON.html

    old new  
     1<html>  
     2<head>  
     3    <script src="../../lib/OpenLayers.js"></script>  
     4    <script type="text/javascript"><!--  
     5 
     6    var poly_content = '{"type": "FeatureCollection", "members": [{"geometry": {"type": "Polygon", "coordinates": [[[-131.484375, -5.9765625], [-112.5, -58.0078125], [-32.34375, -50.2734375], [-114.609375, 52.3828125], [-167.34375, -35.5078125], [-146.953125, -57.3046875], [-139.921875, -34.1015625], [-131.484375, -5.9765625]]]}, "type": "Feature", "id": 562, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}]}';  
     7    var point_feature = '{"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {"strokeColor": "blue", "title": "Feature 5", "author": "Your Name Here"}}' 
     8    var line_feature = '{"type": "FeatureCollection", "members": [{"geometry": {"type": "LineString", "coordinates": [[-27.0703125, 59.4140625], [-77.6953125, 20.7421875], [30.5859375, -36.2109375], [67.1484375, 34.8046875]]}, "type": "Feature", "id": 559, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}]}'; 
     9    var multiple_features = '{"type": "FeatureCollection", "members": [{"geometry": {"type": "Point", "coordinates": [-91.0546875, 43.9453125]}, "type": "Feature", "id": 577, "properties": {"strokeColor": "red", "title": "Feature 2", "image": "foo.gif", "author": "Your Name Here"}}, {"geometry": {"type": "LineString", "coordinates": [[91.40625, -1.40625], [116.015625, -42.890625], [153.28125, -28.125], [108.984375, 11.25], [75.234375, 8.4375], [76.640625, 9.140625], [67.5, -36.5625], [67.5, -35.859375]]}, "type": "Feature", "id": 576, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [139.5703125, 57.48046875]}, "type": "Feature", "id": 575, "properties": {"strokeColor": "blue", "title": "Feature 7", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [107.2265625, 82.44140625]}, "type": "Feature", "id": 574, "properties": {"strokeColor": "blue", "title": "Feature 6", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {"strokeColor": "blue", "title": "Feature 5", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [116.3671875, 61.69921875]}, "type": "Feature", "id": 572, "properties": {"strokeColor": "blue", "title": "Feature 4", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [145.8984375, 73.65234375]}, "type": "Feature", "id": 571, "properties": {"strokeColor": "blue", "title": "Feature 3", "author": "Your Name Here"}}, {"geometry": {"type": "Polygon", "coordinates": [[[32.34375, 52.20703125], [87.1875, 70.13671875], [122.6953125, 37.44140625], [75.234375, 42.36328125], [40.078125, 42.36328125], [28.828125, 48.33984375], [18.6328125, 56.77734375], [23.203125, 65.56640625], [32.34375, 52.20703125]]]}, "type": "Feature", "id": 570, "properties": {"strokeColor": "blue", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [62.578125, -53.4375]}, "type": "Feature", "id": 569, "properties": {"strokeColor": "red", "title": "Feature 3", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [121.640625, 16.875]}, "type": "Feature", "id": 568, "properties": {"strokeColor": "red", "title": "Feature 6", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [135.703125, 8.4375]}, "type": "Feature", "id": 567, "properties": {"strokeColor": "red", "title": "Feature 4", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [137.109375, 48.515625]}, "type": "Feature", "id": 566, "properties": {"strokeColor": "red", "title": "Feature 274", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 565, "properties": {}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 564, "properties": {}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 563, "properties": {}}, {"geometry": {"type": "Polygon", "coordinates": [[[-131.484375, -5.9765625], [-112.5, -58.0078125], [-32.34375, -50.2734375], [-114.609375, 52.3828125], [-167.34375, -35.5078125], [-146.953125, -57.3046875], [-139.921875, -34.1015625], [-131.484375, -5.9765625]]]}, "type": "Feature", "id": 562, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [48.8671875, -15.8203125]}, "type": "Feature", "id": 560, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "LineString", "coordinates": [[-27.0703125, 59.4140625], [-77.6953125, 20.7421875], [30.5859375, -36.2109375], [67.1484375, 34.8046875]]}, "type": "Feature", "id": 559, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [12.65625, 16.5234375]}, "type": "Feature", "id": 558, "properties": {"styleUrl": "#allstyle", "title": "Feature 1", "strokeColor": "red", "author": "Your Name Here"}}]}'; 
     10    var parser = new OpenLayers.Format.GeoJSON(); 
     11 
     12 
     13    function test_Format_GeoJSON_constructor(t) {  
     14        t.plan(4);  
     15          
     16        var options = {'foo': 'bar'};  
     17        var format = new OpenLayers.Format.GeoJSON(options);  
     18        t.ok(format instanceof OpenLayers.Format.GeoJSON,  
     19             "new OpenLayers.Format.GeoJSON returns object" );  
     20        t.eq(format.foo, "bar", "constructor sets options correctly");  
     21        t.eq(typeof format.read, "function", "format has a read function");  
     22        t.eq(typeof format.write, "function", "format has a write function");  
     23    }  
     24     
     25    function test_Format_GeoJSON_valid_type(t) {  
     26        t.plan(13); 
     27        OpenLayers.Console.error = function(error) { window.global_error = error; } 
     28        var types = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "Box"]; 
     29        for (var i = 0; i < types.length; i++) { 
     30            t.ok(parser.isValidType({'type':types[i]}, "Geometry"), "Geometry with type " + types[i] + " is valid"); 
     31        } 
     32        t.ok(!parser.isValidType({'type':"foo"}, "Geometry"), "Geometry with type foo is not valid"); 
     33        t.eq(global_error, "Unsupported geometry type: foo", "error message set correctly for 'foo' geom."); 
     34        t.ok(parser.isValidType({}, "FeatureCollection"), "Feature collection type is always valid");  
     35        t.ok(parser.isValidType({'type':"GeometryCollection"}, "GeometryCollection"), "Geometry Collection type is valid");  
     36        t.ok(!parser.isValidType({'type':"GeometryCollection2"}, "GeometryCollection"), "Geometry Collection 2 type is invalid");  
     37        t.eq(global_error, "Cannot convert types from GeometryCollection2 to GeometryCollection", "error message set correctly for bad geometrycollection type"); 
     38    }     
     39 
     40    function test_Format_GeoJSON_point(t) {  
     41        t.plan(3); 
     42        data = parser.read(point_feature); 
     43        t.eq(data[0].fid, 573, "Fid is correct on point feature");  
     44        t.eq(data[0].geometry.x, 94.21875, 'Reading point feature gives correct x'); 
     45        data = parser.read(point_feature, "Feature"); 
     46        t.eq(data.fid, 573, 'Reading point feature with type gives feature instead of array of features '); 
     47    }     
     48    function test_Format_GeoJSON_line(t) {  
     49        t.plan(5); 
     50        data = parser.read(line_feature); 
     51        t.eq(data[0].fid, 559, "Fid is correct on line feature");  
     52        t.eq(data[0].geometry.components.length, 4, 'Reading line feature gives correct length'); 
     53        t.eq(data[0].geometry.CLASS_NAME, 'OpenLayers.Geometry.LineString', 'Reading line feature gives correct class'); 
     54        t.eq(data[0].geometry.components[0].x, -27.0703125, 'Reading line feature gives correct x'); 
     55        t.eq(data[0].geometry.components[0].y, 59.4140625, 'Reading line feature gives correct y'); 
     56    }   
     57    function test_Format_GeoJSON_poly(t) {  
     58        t.plan(2); 
     59        data = parser.read(poly_content); 
     60        t.eq(data[0].fid, 562, "poly id is correct")  
     61        t.eq(data[0].geometry.components[0].components.length, 8,  
     62                'Reading polygon first ring  on feature from featurecollection gives correct length'); 
     63    } 
     64 
     65    function test_Format_GeoJSON_multipoint(t) {  
     66        t.plan(5); 
     67        var multipoint = { 
     68           "type": "MultiPoint", 
     69           "coordinates": [ 
     70               [100.0, 0.0], [101.0, 1.0] 
     71           ] 
     72        }     
     73        data = parser.read(multipoint, "Geometry"); 
     74        t.eq(data.components.length, 2,  
     75            "Right number of components"); 
     76        t.eq(data.components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "First component is point");     
     77        t.eq(data.components[1].CLASS_NAME, "OpenLayers.Geometry.Point", "Second component is point");     
     78        t.eq(data.components[1].x, 101, "x of second component is right");     
     79        t.eq(data.components[1].y, 1, "y of second component is right");     
     80    } 
     81 
     82  
     83    function test_Format_GeoJSON_multiline(t) { 
     84        t.plan(3); 
     85        var multiline = { 
     86           "type": "MultiLineString", 
     87           "coordinates": [ 
     88               [ [100.0, 0.0], [101.0, 1.0] ], 
     89               [ [102.0, 2.0], [103.0, 3.0] ] 
     90           ] 
     91        }     
     92        data = parser.read(multiline, "Geometry"); 
     93        t.eq(data.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "Correct class retrieved") 
     94        t.eq(data.components[0].components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "correct type of components") 
     95        t.eq(data.components[0].CLASS_NAME, "OpenLayers.Geometry.LineString", "correct type of components") 
     96    } 
     97    function test_Format_GeoJSON_multipol(t) { 
     98        t.plan(2); 
     99        var multipol = { 
     100            "type": "MultiPolygon", 
     101            "coordinates": [ 
     102                [ 
     103                    [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ] 
     104                ], 
     105                [ 
     106                    [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ], 
     107                    [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ] 
     108                ] 
     109            ] 
     110         } 
     111        OpenLayers.Console.error = function(error) { window.global_error = error; } 
     112        data = parser.read(multipol, "Geometry"); 
     113        t.eq(data.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon", "Correct class retrieved") 
     114        t.eq(data.components[1].components[0].components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "correct type of components") 
     115    }     
     116     
     117    function test_Format_GeoJSON_box(t) { 
     118        t.plan(6); 
     119        var box = { 
     120            "type": "Box", 
     121            "coordinates": [[100.0, 0.0], [101.0, 1.0]] 
     122        }; 
     123        var poly = parser.read(box, "Geometry"); 
     124        t.eq(poly.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Box creates polygon");  
     125        t.eq(poly.components[0].components[1].x, 101, "x of lower right is correct"); 
     126        t.eq(poly.components[0].components[1].y, 0, "y of lower right is correct"); 
     127        t.eq(poly.components[0].components[3].x, 100, "x of upper left is correct"); 
     128        t.eq(poly.components[0].components[3].y, 1, "y of upper left is correct"); 
     129        var box = parser.write(poly ); 
     130        t.ok(box.search("Polygon") != -1 , "Serializes back to polygon"); 
     131    }     
     132        
     133           
     134 
     135     
     136    // This test is from the geom_collection example on geojson spec. 
     137    function test_Format_GeoJSON_geom_collection(t) { 
     138       t.plan(7); 
     139       var geomcol = { 
     140           "type": "GeometryCollection", 
     141           "members": [ 
     142               { 
     143                   "type": "Point", 
     144                   "coordinates": [100.0, 0.0] 
     145               }, 
     146               { 
     147                   "type": "LineString", 
     148                   "coordinates": [ 
     149                       [101.0, 0.0], [102.0, 1.0] 
     150                   ] 
     151               } 
     152           ] 
     153        } 
     154        data = parser.read(geomcol, "GeometryCollection"); 
     155        t.eq(data[0].CLASS_NAME,  
     156              "OpenLayers.Geometry.Point", "First geom in geom collection is point type");   
     157        t.eq(data[0].x, 100, "First geom in geom collection has correct x");   
     158        t.eq(data[0].y, 0, "First geom in geom collection has correct x");   
     159         
     160        t.eq(data[1].CLASS_NAME,  
     161              "OpenLayers.Geometry.LineString", "Second geom in geom collection is point linestring");   
     162        t.eq(data[1].components.length, 2, "linestring is correct length"); 
     163        t.eq(data[1].components[1].x, 102, "linestring is correct x end"); 
     164        t.eq(data[1].components[1].y, 1, "linestring is correct y end"); 
     165         
     166    } 
     167     
     168    function test_Format_GeoJSON_multipleFeatures(t) { 
     169        t.plan(2); 
     170        var feats = parser.read(multiple_features); 
     171        t.eq(feats.length, 19, "parsing a feature collection returns the correct number of features."); 
     172        var types = {'Point':0, 'LineString':0, 'Polygon':0} 
     173        for(var i = 0; i < feats.length; i++) { 
     174            var type = feats[i].geometry.CLASS_NAME.replace("OpenLayers.Geometry.", ""); 
     175            types[type]++; 
     176        } 
     177        t.eq(types, {'Point':15, 'Polygon': 2, 'LineString':2}, "Correct number of each type");  
     178    } 
     179     
     180    function test_Format_GeoJSON_write(t) { 
     181        t.plan(10); 
     182        var line_object = {"type": "FeatureCollection",  
     183                           "members": [{"geometry":  
     184                               {"type": "LineString",  
     185                                "coordinates": [[-27.0703125, 59.4140625],  
     186                                                [-77.6953125, 20.7421875],  
     187                                                [30.5859375, -36.2109375],  
     188                                                [67.1484375, 34.8046875]]},  
     189                                "type": "Feature",  
     190                                "id": 559,  
     191                                "properties":  
     192                                  { 
     193                                    "strokeColor": "red",  
     194                                    "title": "Feature 1",  
     195                                    "author": "Your Name Here"}}]}; 
     196        data = parser.read(line_object); 
     197        out = parser.write(data); 
     198        serialized = '{"type":"FeatureCollection","members":[{"type":"Feature","id":559,"properties":{"strokeColor":"red","title":"Feature 1","author":"Your Name Here"},"geometry":{"type":"LineString","coordinates":[[-27.0703125,59.4140625],[-77.6953125,20.7421875],[30.5859375,-36.2109375],[67.1484375,34.8046875]]}}]}'; 
     199        t.eq(out, serialized, "input and output on line collections are the same"); 
     200         
     201        var serialize_tests = [ 
     202         [new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2)),  
     203           '{"type":"Feature","id":0,"properties":{},"geometry":{"type":"Point","coordinates":[1,2]}}'], 
     204         [new OpenLayers.Geometry.Point(1,2),  
     205             '{"type":"Point","coordinates":[1,2]}'],  
     206         [new OpenLayers.Geometry.MultiPoint([new OpenLayers.Geometry.Point(1,2)]),  
     207           '{"type":"MultiPoint","coordinates":[[1,2]]}'],  
     208         [new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(1,2), new OpenLayers.Geometry.Point(3,4)]),  
     209           '{"type":"LineString","coordinates":[[1,2],[3,4]]}'],  
     210         [new OpenLayers.Geometry.Polygon([ 
     211             new OpenLayers.Geometry.LinearRing([ 
     212                 new OpenLayers.Geometry.Point(1,2),  
     213                 new OpenLayers.Geometry.Point(3,4),  
     214                 new OpenLayers.Geometry.Point(5,6)])]),  
     215           '{"type":"Polygon","coordinates":[[[1,2],[3,4],[5,6],[1,2]]]}'] 
     216        ]; 
     217        serialize_tests[0][0].fid = 0; 
     218        multiline = new OpenLayers.Geometry.MultiLineString([serialize_tests[3][0], serialize_tests[3][0]]); 
     219        serialize_tests.push([multiline, '{"type":"MultiLineString","coordinates":[[[1,2],[3,4]],[[1,2],[3,4]]]}']); 
     220        multipolygon = new OpenLayers.Geometry.MultiPolygon([serialize_tests[4][0],  serialize_tests[4][0]]); 
     221        serialize_tests.push([multipolygon, '{"type":"MultiPolygon","coordinates":[[[[1,2],[3,4],[5,6],[1,2]]],[[[1,2],[3,4],[5,6],[1,2]]]]}']); 
     222        serialize_tests.push([ [ serialize_tests[0][0] ], '{"type":"FeatureCollection","members":[{"type":"Feature","id":0,"properties":{},"geometry":{"type":"Point","coordinates":[1,2]}}]}' ]); 
     223        serialize_tests.push([ [ serialize_tests[1][0], serialize_tests[2][0] ], '{"type":"GeometryCollection","members":[{"type":"Point","coordinates":[1,2]},{"type":"MultiPoint","coordinates":[[1,2]]}]}' ]); 
     224        for (var i = 0; i < serialize_tests.length; i++) { 
     225            var input = serialize_tests[i][0]; 
     226            var output = serialize_tests[i][1]; 
     227            test_out = parser.write(input); 
     228            t.eq(test_out, output, "Serializing " + input.toString() + " saved correctly."); 
     229        } 
     230    }     
     231     
     232     
     233    function test_Format_GeoJSON_read_object(t) { 
     234        t.plan(1); 
     235        var line_object = {"type": "FeatureCollection",  
     236                           "members": [{"geometry":  
     237                               {"type": "LineString",  
     238                                "coordinates": [[-27.0703125, 59.4140625],  
     239                                                [-77.6953125, 20.7421875],  
     240                                                [30.5859375, -36.2109375],  
     241                                                [67.1484375, 34.8046875]]},  
     242                                "type": "Feature",  
     243                                "id": 559,  
     244                                "properties":  
     245                                  { 
     246                                    "strokeColor": "red",  
     247                                    "title": "Feature 1",  
     248                                    "author": "Your Name Here"}}]}; 
     249        data = parser.read(line_object); 
     250        t.eq(data[0].fid, 559, "Can read data from an object correctly."); 
     251    }   
     252 
     253    function test_Format_GeoJSON_read_attributes(t) { 
     254        t.plan(3); 
     255        var parser = new OpenLayers.Format.GeoJSON(); 
     256        data = parser.read(line_feature); 
     257        t.eq(data[0].attributes['strokeColor'], 'red', 'read strokeColor attribute properly');  
     258        t.eq(data[0].attributes['title'], 'Feature 1', 'read title attribute properly');  
     259        t.eq(data[0].attributes['author'], 'Your Name Here', 'read author attribute properly');  
     260    } 
     261 
     262    // -->  
     263    </script>  
     264</head>  
     265<body>  
     266</body>  
     267</html>  
  • tests/Format/test_JSON.html

    old new  
     1<html>  
     2<head>  
     3    <script src="../../lib/OpenLayers.js"></script>  
     4    <script type="text/javascript"><!--  
     5 
     6 
     7      
     8    function test_Format_JSON_constructor(t) {  
     9        t.plan(4);  
     10          
     11        var options = {'foo': 'bar'};  
     12        var format = new OpenLayers.Format.JSON(options);  
     13        t.ok(format instanceof OpenLayers.Format.JSON,  
     14             "new OpenLayers.Format.JSON returns object" );  
     15        t.eq(format.foo, "bar", "constructor sets options correctly");  
     16        t.eq(typeof format.read, "function", "format has a read function");  
     17        t.eq(typeof format.write, "function", "format has a write function");  
     18    }  
     19    function test_Format_JSON_parser(t) {  
     20        t.plan(2);  
     21          
     22        var format = new OpenLayers.Format.JSON();  
     23        var data = format.read('{"a":["b"], "c":1}'); 
     24        var obj = {"a":["b"], "c":1}; 
     25        t.eq(obj['a'], data['a'], "element with array parsed correctly.");   
     26        t.eq(obj['c'], data['c'], "element with number parsed correctly.");   
     27    }     
     28    function test_Format_JSON_writer(t) {  
     29        t.plan(1);  
     30          
     31        var format = new OpenLayers.Format.JSON();  
     32        var data = format.write({"a":["b"], "c":1}); 
     33        var obj = '{"a":["b"],"c":1}'; 
     34        t.eq(data, obj, "writing data to json works."); 
     35    } 
     36    // -->  
     37    </script>  
     38</head>  
     39<body>  
     40</body>  
     41</html>  
  • tests/list-tests.html

    old new  
    2222    <li>test_Format.html</li> 
    2323    <li>Format/test_XML.html</li> 
    2424    <li>Format/test_GeoRSS.html</li> 
     25    <li>Format/test_JSON.html</li> 
     26    <li>Format/test_GeoJSON.html</li> 
    2527    <li>Format/test_GML.html</li> 
    2628    <li>Format/test_WKT.html</li> 
    2729    <li>test_Icon.html</li> 
  • lib/OpenLayers/Format/GeoJSON.js

    old new  
     1/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license. 
     2 * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt  
     3 * for the full text of the license. */ 
     4 
     5/** 
     6 * @requires OpenLayers/Format/JSON.js 
     7 * 
     8 * Class: OpenLayers.Format.GeoJSON 
     9 * Read and write GeoJSON. Create a new parser with the 
     10 * <OpenLayers.Format.GeoJSON> constructor. 
     11 * 
     12 * Inherits from: 
     13 *  - <OpenLayers.Format.JSON> 
     14 */ 
     15OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { 
     16 
     17    /** 
     18     * Constructor: OpenLayers.Format.GeoJSON 
     19     * Create a new parser for GeoJSON 
     20     * 
     21     * Parameters: 
     22     * options - {Object} An optional object whose properties will be set on 
     23     *                    this instance. 
     24     */ 
     25    initialize: function(options) { 
     26        OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]); 
     27    }, 
     28 
     29    /** 
     30     * APIMethod: read 
     31     * Deserialize a GeoJSON string. 
     32     * 
     33     * Parameters: 
     34     * json - {String} A GeoJSON string 
     35     * type {String} Optional string that determines the structure of 
     36     *     the output.  Supported values are "Geometry", 
     37     *     "Feature", "GeometryCollection", and 
     38     *     "FeatureCollection".  If absent or null, a 
     39     *     default of "FeatureCollection" is assumed. 
     40     * filter - {Function} A function which will be called for every key 
     41     *     and value at every level of the final result. 
     42     *     Each value will be replaced by the result of 
     43     *     the filter function. This can be used to reform 
     44     *     generic objects into instances of classes, or to 
     45     *     transform date strings into Date objects. 
     46     * 
     47     * Return:  
     48     * {Object} The return depends on the value of the type argument. 
     49     *     If type is "FeatureCollection" (the default), the 
     50     *     return will be an array of OpenLayers.Feature.Vector. 
     51     *     If type is "Geometry", the input json must represent a 
     52     *     single geometry, and the return will be an 
     53     *     OpenLayers.Geometry.  If type is "Feature", the input 
     54     *     json must represent a single feature, and the return 
     55     *     will be an OpenLayers.Feature.Vector.  If type is 
     56     *     "GeometryCollection", the input json must represent 
     57     *     a geometry collection, and the return will be an array 
     58     *     of OpenLayers.Geometry. 
     59     */ 
     60    read: function(json, type, filter) { 
     61        type = (type) ? type : "FeatureCollection"; 
     62        var results = null; 
     63        var obj = null; 
     64        if (typeof json == "string") { 
     65            obj = OpenLayers.Format.JSON.prototype.read.apply(this, 
     66                                                              [json, filter]); 
     67        } else {  
     68            obj = json; 
     69        }     
     70        if(!obj) { 
     71            OpenLayers.Console.error("Bad JSON: " + json); 
     72        } else if(typeof(obj.type) != "string") { 
     73            OpenLayers.Console.error("Bad GeoJSON - no type: " + json); 
     74        } else if(this.isValidType(obj, type)) { 
     75            switch(type) { 
     76                case "Geometry": 
     77                    try { 
     78                        results = this.parseGeometry(obj); 
     79                    } catch(err) { 
     80                        OpenLayers.Console.error(err); 
     81                    } 
     82                    break; 
     83                case "Feature": 
     84                    try { 
     85                        results = this.parseFeature(obj); 
     86                        results.type = "Feature"; 
     87                    } catch(err) { 
     88                        OpenLayers.Console.error(err); 
     89                    } 
     90                    break; 
     91                case "GeometryCollection": 
     92                    results = []; 
     93                    for(var i=0; i<obj.members.length; ++i) { 
     94                        try { 
     95                            results.push(this.parseGeometry(obj.members[i])); 
     96                        } catch(err) { 
     97                            results = null; 
     98                            OpenLayers.Console.error(err); 
     99                        } 
     100                    } 
     101                    break; 
     102                case "FeatureCollection": 
     103                    // for type FeatureCollection, we allow input to be any type 
     104                    results = []; 
     105                    switch(obj.type) { 
     106                        case "Feature": 
     107                            try { 
     108                                results.push(this.parseFeature(obj)); 
     109                            } catch(err) { 
     110                                results = null; 
     111                                OpenLayers.Console.error(err); 
     112                            } 
     113                            break; 
     114                        case "FeatureCollection": 
     115                            for(var i=0; i<obj.members.length; ++i) { 
     116                                try { 
     117                                    results.push(this.parseFeature(obj.members[i])); 
     118                                } catch(err) { 
     119                                    results = null; 
     120                                    OpenLayers.Console.error(err); 
     121                                } 
     122                            } 
     123                            break; 
     124                        case "GeometryCollection": 
     125                            for(var i=0; i<obj.members.length; ++i) { 
     126                                try { 
     127                                    var geom = this.parseGeometry(obj.members[i]); 
     128                                    results.push(new OpenLayers.Feature.Vector(geom)); 
     129                                } catch(err) { 
     130                                    results = null; 
     131                                    OpenLayers.Console.error(err); 
     132                                } 
     133                            } 
     134                            break; 
     135                        default: 
     136                            try { 
     137                                var geom = this.parseGeometry(obj); 
     138                                results.push(new OpenLayers.Feature.Vector(geom)); 
     139                            } catch(err) { 
     140                                results = null; 
     141                                OpenLayers.Console.error(err); 
     142                            } 
     143                    } 
     144                break; 
     145            } 
     146        } 
     147        return results; 
     148    }, 
     149     
     150    /** 
     151     * Method: isValidType 
     152     * Check if a GeoJSON object is a valid representative of the given type 
     153     * 
     154     * Return: 
     155     * {Boolean} The object is valid GeoJSON object of the given type 
     156     */ 
     157    isValidType: function(obj, type) { 
     158        var valid = false; 
     159        switch(type) { 
     160            case "Geometry": 
     161                if(OpenLayers.Util.indexOf(["Point", "MultiPoint", "LineString", 
     162                                            "MultiLineString", "Polygon", 
     163                                            "MultiPolygon", "Box"], obj.type) == -1) { 
     164                    // unsupported geometry type 
     165                    OpenLayers.Console.error("Unsupported geometry type: " + 
     166                                              obj.type); 
     167                } else { 
     168                    valid = true; 
     169                } 
     170                break; 
     171            case "FeatureCollection": 
     172                // allow for any type to be converted to a feature collection 
     173                valid = true; 
     174                break 
     175            default: 
     176                // for GeometryCollection and Feature, types must match 
     177                if(obj.type == type) { 
     178                    valid = true; 
     179                } else { 
     180                    OpenLayers.Console.error("Cannot convert types from " + 
     181                                              obj.type + " to " + type); 
     182                } 
     183        } 
     184        return valid; 
     185    }, 
     186     
     187    /** 
     188     * Method: parseFeature 
     189     * Convert a feature object from GeoJSON into an <OpenLayers.Feature.Vector>. 
     190     * 
     191     * Parameters: 
     192     * obj - {Object} An object created from a GeoJSON object 
     193     * 
     194     * Return: 
     195     * {OpenLayers.Feature.Vector} A feature 
     196     */ 
     197    parseFeature: function(obj) { 
     198        var feature, geometry, attributes; 
     199        attributes = (obj.properties) ? obj.properties : {}; 
     200        try { 
     201            geometry = this.parseGeometry(obj.geometry);             
     202        } catch(err) { 
     203            // deal with bad geometries 
     204            throw err; 
     205        } 
     206        feature = new OpenLayers.Feature.Vector(geometry, attributes); 
     207        if(obj.id) { 
     208            feature.fid = obj.id; 
     209        } 
     210        return feature; 
     211    }, 
     212     
     213    /** 
     214     * Method: parseGeometry 
     215     * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>. 
     216     * 
     217     * Parameters: 
     218     * obj - {Object} An object created from a GeoJSON object 
     219     * 
     220     * Return:  
     221     * {OpenLayers.Geometry} A geometry 
     222     */ 
     223    parseGeometry: function(obj) { 
     224        var geometry; 
     225        if(!(obj.coordinates instanceof Array)) { 
     226            throw "Geometry must have coordinates array: " + obj; 
     227        } 
     228        if(!this.parseCoords[obj.type.toLowerCase()]) { 
     229            throw "Unsupported geometry type: " + obj.type; 
     230        } 
     231        try { 
     232            geometry = this.parseCoords[obj.type.toLowerCase()].apply(this, [obj.coordinates]); 
     233        } catch(err) { 
     234            // deal with bad coordinates 
     235            throw err; 
     236        } 
     237        return geometry; 
     238    }, 
     239     
     240    /** 
     241     * Property: parseCoords 
     242     * Object with properties corresponding to the GeoJSON geometry types. 
     243     * Property values are functions that do the actual parsing. 
     244     */ 
     245    parseCoords: { 
     246        /** 
     247         * Method: parseCoords.point 
     248         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     249         * 
     250         * Parameters: 
     251         * array - {Object} The coordinates array from the GeoJSON fragment 
     252         * 
     253         * Return: 
     254         * {OpenLayers.Geometry} A geometry 
     255         */ 
     256        "point": function(array) { 
     257            if(array.length != 2) { 
     258                throw "Only 2D points are supported: " + array; 
     259            } 
     260            return new OpenLayers.Geometry.Point(array[0], array[1]); 
     261        }, 
     262         
     263        /** 
     264         * Method: parseCoords.multipoint 
     265         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     266         * 
     267         * Parameters: 
     268         * array {Object} The coordinates array from the GeoJSON fragment 
     269         * 
     270         * Return: 
     271         * {OpenLayers.Geometry} A geometry 
     272         */ 
     273        "multipoint": function(array) { 
     274            var points = []; 
     275            var p = null; 
     276            for(var i=0; i<array.length; ++i) { 
     277                try { 
     278                    p = this.parseCoords["point"].apply(this, [array[i]]); 
     279                } catch(err) { 
     280                    throw err; 
     281                } 
     282                points.push(p); 
     283            } 
     284            return new OpenLayers.Geometry.MultiPoint(points); 
     285        }, 
     286 
     287        /** 
     288         * Method: parseCoords.linestring 
     289         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     290         * 
     291         * Parameters: 
     292         * array - {Object} The coordinates array from the GeoJSON fragment 
     293         * 
     294         * Return: 
     295         * {OpenLayers.Geometry} A geometry 
     296         */ 
     297        "linestring": function(array) { 
     298            var points = []; 
     299            var p = null; 
     300            for(var i=0; i<array.length; ++i) { 
     301                try { 
     302                    p = this.parseCoords["point"].apply(this, [array[i]]); 
     303                } catch(err) { 
     304                    throw err; 
     305                } 
     306                points.push(p); 
     307            } 
     308            return new OpenLayers.Geometry.LineString(points); 
     309        }, 
     310         
     311        /** 
     312         * Method: parseCoords.multilinestring 
     313         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     314         * 
     315         * Parameters: 
     316         * array - {Object} The coordinates array from the GeoJSON fragment 
     317         * 
     318         * Return: 
     319         * {OpenLayers.Geometry} A geometry 
     320         */ 
     321        "multilinestring": function(array) { 
     322            var lines = []; 
     323            var l = null; 
     324            for(var i=0; i<array.length; ++i) { 
     325                try { 
     326                    l = this.parseCoords["linestring"].apply(this, [array[i]]); 
     327                } catch(err) { 
     328                    throw err; 
     329                } 
     330                lines.push(l); 
     331            } 
     332            return new OpenLayers.Geometry.MultiLineString(lines); 
     333        }, 
     334         
     335        /** 
     336         * Method: parseCoords.polygon 
     337         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     338         * 
     339         * Return: 
     340         * array - {Object} The coordinates array from the GeoJSON fragment 
     341         * 
     342         * Return: 
     343         * {OpenLayers.Geometry} A geometry 
     344         */ 
     345        "polygon": function(array) { 
     346            var rings = []; 
     347            var r, l; 
     348            for(var i=0; i<array.length; ++i) { 
     349                try { 
     350                    l = this.parseCoords["linestring"].apply(this, [array[i]]); 
     351                } catch(err) { 
     352                    throw err; 
     353                } 
     354                r = new OpenLayers.Geometry.LinearRing(l.components); 
     355                rings.push(r); 
     356            } 
     357            return new OpenLayers.Geometry.Polygon(rings); 
     358        }, 
     359 
     360        /** 
     361         * Method: parseCoords.multipolygon 
     362         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     363         * 
     364         * Parameters: 
     365         * array - {Object} The coordinates array from the GeoJSON fragment 
     366         * 
     367         * Return: 
     368         * {OpenLayers.Geometry} A geometry 
     369         */ 
     370        "multipolygon": function(array) { 
     371            var polys = []; 
     372            var p = null; 
     373            for(var i=0; i<array.length; ++i) { 
     374                try { 
     375                    p = this.parseCoords["polygon"].apply(this, [array[i]]); 
     376                } catch(err) { 
     377                    throw err; 
     378                } 
     379                polys.push(p); 
     380            } 
     381            return new OpenLayers.Geometry.MultiPolygon(polys); 
     382        }, 
     383 
     384        /** 
     385         * Method: parseCoords.box 
     386         * Convert a coordinate array from GeoJSON into an OpenLayers.Geometry. 
     387         * 
     388         * Parameters: 
     389         * array - {Object} The coordinates array from the GeoJSON fragment 
     390         * 
     391         * Return: 
     392         * {OpenLayers.Geometry} A geometry 
     393         */ 
     394        "box": function(array) { 
     395            if(array.length != 2) { 
     396                throw "GeoJSON box coordinates must have 2 elements"; 
     397            } 
     398            return new OpenLayers.Geometry.Polygon([ 
     399                new OpenLayers.Geometry.LinearRing([ 
     400                    new OpenLayers.Geometry.Point(array[0][0], array[0][1]), 
     401                    new OpenLayers.Geometry.Point(array[1][0], array[0][1]), 
     402                    new OpenLayers.Geometry.Point(array[1][0], array[1][1]), 
     403                    new OpenLayers.Geometry.Point(array[0][0], array[1][1]), 
     404                    new OpenLayers.Geometry.Point(array[0][0], array[0][1]) 
     405                ]) 
     406            ]); 
     407        } 
     408 
     409    }, 
     410 
     411    /** 
     412     * APIMethod: write 
     413     * Serialize a feature, geometry, array of features, or array of geometries 
     414     *     into a GeoJSON string. 
     415     * 
     416     * Parameters: 
     417     * obj - {Object} An OpenLayers.Feature.Vector, OpenLayers.Geometry, 
     418     *     or an array of either features or geometries. 
     419     * pretty - {Boolean} Structure the output with newlines and indentation. 
     420     *     Default is false. 
     421     * 
     422     * Return: 
     423     * {String} The GeoJSON string representation of the input geometry, 
     424     *     features, array of geometries, or array of features. 
     425     */ 
     426    write: function(obj, pretty) { 
     427        var geojson = { 
     428            "type": null 
     429        }; 
     430        if(obj instanceof Array) { 
     431            geojson.members = []; 
     432            for(var i=0; i<obj.length; ++i) { 
     433                var element = obj[i]; 
     434                if(element instanceof OpenLayers.Feature.Vector) { 
     435                    if(geojson.type == null) { 
     436                        geojson.type = "FeatureCollection"; 
     437                        if(element.layer && element.layer.projection) { 
     438                            var proj = element.layer.projection; 
     439                            if(proj.match(/epsg:/i)) { 
     440                                geojson.crs = { 
     441                                    "type": "EPSG", 
     442                                    "properties": { 
     443                                        "code": parseInt(proj.substring(proj.indexOf(":") + 1)) 
     444                                    } 
     445                                }; 
     446                            } 
     447                        } 
     448                    } else if(geojson.type != "FeatureCollection") { 
     449                        OpenLayers.Console.error("FeatureCollection only supports collections of features: " + element); 
     450                        break; 
     451                    } 
     452                    geojson.members.push(this.extract.feature.apply(this, [element])); 
     453                } else if (element.CLASS_NAME.search("OpenLayers.Geometry") == 0) { 
     454                    if(geojson.type == null) { 
     455                        geojson.type = "GeometryCollection"; 
     456                    } else if(geojson.type != "GeometryCollection") { 
     457                        OpenLayers.Console.error("GeometryCollection only supports collections of geometries: " + element); 
     458                        break; 
     459                    } 
     460                    geojson.members.push(this.extract.geometry.apply(this, [element])); 
     461                } 
     462            } 
     463        } else if (obj.CLASS_NAME.search("OpenLayers.Geometry") == 0) { 
     464            geojson = this.extract.geometry.apply(this, [obj]); 
     465        } else if (obj instanceof OpenLayers.Feature.Vector) { 
     466            geojson = this.extract.feature.apply(this, [obj]); 
     467            if(obj.layer && obj.layer.projection) { 
     468                var proj = obj.layer.projection; 
     469                if(proj.match(/epsg:/i)) { 
     470                    geojson.crs = { 
     471                        "type": "EPSG", 
     472                        "properties": { 
     473                            "code": parseInt(proj.substring(proj.indexOf(":") + 1)) 
     474                        } 
     475                    }; 
     476                } 
     477            } 
     478        } 
     479        return OpenLayers.Format.JSON.prototype.write.apply(this, 
     480                                                            [geojson, pretty]); 
     481    }, 
     482     
     483    /** 
     484     * Property: extract 
     485     * Object with properties corresponding to the GeoJSON types. 
     486     * Property values are functions that do the actual value extraction. 
     487     */ 
     488    extract: { 
     489        /** 
     490         * Method: extract.feature 
     491         * Return a partial GeoJSON object representing a single feature. 
     492         * 
     493         * Parameters: 
     494