Created
August 20, 2020 12:57
-
-
Save MangelMaxime/23fa8091574be9346b852d9027fbb76a to your computer and use it in GitHub Desktop.
Created with Fable REPL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open Thoth.Json | |
open Fable.Core | |
open Fable.Core.JS | |
open Fable.Core.JsInterop | |
// Code copied from https://github.com/MangelMaxime/Fable.Geojson/blob/master/src/Geojson.fs | |
// REPL include Geojson library so I copy/paste the code in order to have equivalent | |
module GeoJson = | |
type [<StringEnum>] [<RequireQualifiedAccess>] GeoJsonGeometryTypes = | |
| [<CompiledName "Point">] Point | |
| [<CompiledName "LineString">] LineString | |
| [<CompiledName "MultiPoint">] MultiPoint | |
| [<CompiledName "Polygon">] Polygon | |
| [<CompiledName "MultiLineString">] MultiLineString | |
| [<CompiledName "MultiPolygon">] MultiPolygon | |
| [<CompiledName "GeometryCollection">] GeometryCollection | |
type GeoJsonTypes = | |
U3<string, string, GeoJsonGeometryTypes> | |
type Position = | |
ResizeArray<float> | |
type BBox = | |
U2<float * float * float * float, float * float * float * float * float * float> | |
/// The base GeoJSON object. | |
/// https://tools.ietf.org/html/rfc7946#section-3 | |
/// The GeoJSON specification also allows foreign members | |
/// (https://tools.ietf.org/html/rfc7946#section-6.1) | |
/// Developers should use "&" type in TypeScript or extend the interface | |
/// to add these foreign members. | |
type [<AllowNullLiteral>] GeoJsonObject = | |
/// Specifies the type of GeoJSON object. | |
abstract ``type``: GeoJsonTypes with get, set | |
/// Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections. | |
/// https://tools.ietf.org/html/rfc7946#section-5 | |
abstract bbox: BBox option with get, set | |
/// A geometry object. | |
/// https://tools.ietf.org/html/rfc7946#section-3 | |
type [<AllowNullLiteral>] GeometryObject = | |
inherit GeoJsonObject | |
abstract ``type``: GeoJsonGeometryTypes with get, set | |
/// Point geometry object. | |
/// https://tools.ietf.org/html/rfc7946#section-3.1.2 | |
type [<AllowNullLiteral>] Point = | |
inherit GeometryObject | |
abstract ``type``: string with get, set | |
abstract coordinates: Position with get, set | |
/// A feature object which contains a geometry and associated properties. | |
/// https://tools.ietf.org/html/rfc7946#section-3.2 | |
type [<AllowNullLiteral>] Feature<'G, 'P> = | |
inherit GeoJsonObject | |
abstract ``type``: string with get, set | |
/// The feature's geometry | |
abstract geometry: 'G with get, set | |
/// A value that uniquely identifies this feature in a | |
/// https://tools.ietf.org/html/rfc7946#section-3.2. | |
abstract id: U2<string, float> option with get, set | |
/// Properties associated with this feature. | |
abstract properties: 'P with get, set | |
// In your project you can use type abbreviation to attach your encoder/decoder to the types | |
type GeoJson.GeoJsonGeometryTypes with | |
static member Decoder = | |
Decode.string | |
|> Decode.andThen (fun value -> | |
match value with | |
| "Point" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.Point | |
| "LineString" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.LineString | |
| "MultiPoint" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.MultiPoint | |
| "Polygon" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.Polygon | |
| "MultiLineString" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.MultiLineString | |
| "MultiPolygon" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.MultiPolygon | |
| "GeometryCollection" -> Decode.succeed GeoJson.GeoJsonGeometryTypes.GeometryCollection | |
| invalid -> | |
"`" + invalid + "` is not a valid value for GeoJson.GeoJsonGeometryTypes" | |
|> Decode.fail | |
) | |
// Homwever, with all the inerithance in the library it seems like the compiler is confused | |
// Example: | |
// type GeoJson.GeoJsonObject with | |
// static member Decoder = | |
// Decode.object (fun get -> | |
// // Use Fable interop to create the object instance | |
// // You can use createEmpty, jsOption, etc. | |
// let res = createEmpty<GeoJson.GeoJsonObject> | |
// res.``type`` <- get.Required.Field "type" GeoJsonTypes.Decoder | |
// res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
// // I am not sure which version will work in your case | |
// // I think get.Optional.Field should do the job but I mention this one just in case | |
// // res.bbox <- get.Required.Field "bbox" (Decode.option BBox.Decoder) | |
// ) | |
// type GeoJson.GeometryObject with | |
// static member Decoder = | |
// Decode.object (fun get -> | |
// let res = createEmpty<GeoJson.GeometryObject> | |
// res.``type`` <- get.Required.Field "type" GeoJson.GeoJsonGeometryTypes.Decoder | |
// res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
// ) | |
// When calling GeoJson.GeometryObject.Decoder F# compiler don't seems to be able to choose between | |
// GeoJson.GeoJsonObject.Decoder and GeoJson.GeometryObject.Decoder | |
// Because GeoJson.GeometryObject inherit from GeoJson.GeoJsonObject | |
// You can also just use module with function to mimic the type structure (for intellisense purpose) | |
// Or just use function: | |
// let GeoJsonTypesGeoJsonTypes : Decoder<GeoJson.GeoJsonTypes> = ... | |
module GeoJsonTypes = | |
let Decoder : Decoder<GeoJson.GeoJsonTypes> = | |
// You can use oneOf in order to support U2, U3, ... types | |
// If you know you want to use only one of the version you don't need to code all of them | |
Decode.oneOf [ | |
GeoJson.GeoJsonGeometryTypes.Decoder |> Decode.map U3.Case3 | |
Decode.string |> Decode.map U3.Case1 | |
] | |
module Position = | |
let Decoder : Decoder<GeoJson.Position> = | |
Decode.array Decode.float | |
|> Decode.map ResizeArray | |
module BBox = | |
let Decoder : Decoder<GeoJson.BBox> = | |
Decode.oneOf [ | |
Decode.tuple4 | |
Decode.float | |
Decode.float | |
Decode.float | |
Decode.float | |
|> Decode.map U2.Case1 | |
Decode.tuple6 | |
Decode.float | |
Decode.float | |
Decode.float | |
Decode.float | |
Decode.float | |
Decode.float | |
|> Decode.map U2.Case2 | |
] | |
module GeoJsonObject = | |
let Decoder : Decoder<GeoJson.GeoJsonObject> = | |
Decode.object (fun get -> | |
// Use Fable interop to create the object instance | |
// You can use createEmpty, jsOption, etc. | |
let res = createEmpty<GeoJson.GeoJsonObject> | |
res.``type`` <- get.Required.Field "type" GeoJsonTypes.Decoder | |
res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
res | |
// I am not sure which version will work in your case | |
// I think get.Optional.Field should do the job but I mention this one just in case | |
// res.bbox <- get.Required.Field "bbox" (Decode.option BBox.Decoder) | |
) | |
module GeometryObject = | |
let Decoder : Decoder<GeoJson.GeometryObject> = | |
Decode.object (fun get -> | |
let res = createEmpty<GeoJson.GeometryObject> | |
res.``type`` <- get.Required.Field "type" GeoJson.GeoJsonGeometryTypes.Decoder | |
res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
res | |
) | |
module Point = | |
let Decoder : Decoder<GeoJson.Point> = | |
Decode.object (fun get -> | |
let res = createEmpty<GeoJson.Point> | |
res.``type`` <- get.Required.Field "type" Decode.string | |
res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
res.coordinates <- get.Required.Field "coordinates" Position.Decoder | |
res | |
) | |
module Feature = | |
let Decoder<'G, 'P> (geometryDecoder : Decoder<'G>) (propertiesDecoder : Decoder<'P>) : Decoder<GeoJson.Feature<'G, 'P>> = | |
Decode.object (fun get -> | |
let idDecoder = | |
Decode.oneOf [ | |
Decode.string |> Decode.map U2.Case1 | |
Decode.float |> Decode.map U2.Case2 | |
] | |
let res = createEmpty<GeoJson.Feature<'G, 'P>> | |
res.``type`` <- get.Required.Field "type" Decode.string | |
res.geometry <- get.Required.Field "geometry" geometryDecoder | |
res.id <- get.Optional.Field "id" idDecoder | |
res.bbox <- get.Optional.Field "bbox" BBox.Decoder | |
res.properties <- get.Required.Field "properties" propertiesDecoder | |
res | |
) | |
// This JSON has been copied from https://geojson.org/ | |
// So I guess it should be valid | |
let json = | |
""" | |
{ | |
"type": "Feature", | |
"geometry": { | |
"type": "Point", | |
"coordinates": [125.6, 10.1] | |
}, | |
"properties": { | |
"name": "Dinagat Islands" | |
} | |
} | |
""" | |
// You can use anonymous record if you don't want to create a real type for some of the values | |
let myPropertiesDecoder = | |
Decode.object (fun get -> | |
{| | |
name = get.Required.Field "name" Decode.string | |
|} | |
) | |
match Decode.fromString (Feature.Decoder Point.Decoder myPropertiesDecoder) json with | |
| Ok value -> | |
printfn "id: %A" value.id | |
JS.console.log("geometry: ", value.geometry) | |
printfn "geometry.type: %A" value.geometry.``type`` | |
printfn "geometry.coordinates: %A" value.geometry.coordinates | |
printfn "properties: %A" value.properties | |
| Error err -> | |
JS.console.log err |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Fable + Recharts</title> | |
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head> | |
<body> | |
<div id="container1"></div> | |
<script src="bundle.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment