Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geopoint support #112

Open
kongweiying2 opened this issue Aug 3, 2021 · 18 comments
Open

Geopoint support #112

kongweiying2 opened this issue Aug 3, 2021 · 18 comments
Labels
good first issue Good for newcomers Sponsorable If you need this feature, you can contact us for sponsoring it. Or submit a PR!

Comments

@kongweiying2
Copy link

I understand that Geopoint isn't currently supported, however is there a potential roadmap for adding support? Or does the custom view support allow us to add it ourselves?

@fgatti675
Copy link
Member

fgatti675 commented Aug 5, 2021

Hi @TheAussieStew
Geopoint support is currently not in the roadmap.
In any case, you should be able to implement your own custom field supporting it :)
https://firecms.co/docs/entities/custom_fields
Let me know if you have an implementation so we can try to merge it into the library

@kongweiying2
Copy link
Author

Sounds good!

@fgatti675 fgatti675 added the good first issue Good for newcomers label Nov 23, 2021
@fgatti675
Copy link
Member

I am marking this feature as a good first issue.
It can be implemented in the same way that a developer would implement a "custom field": https://firecms.co/docs/entities/custom_fields
That means that it is not necessary to develop the feature in the source code (though that would be the best!)

@SKLn-Rad
Copy link

SKLn-Rad commented Jun 4, 2022

Anyone manage to get a working implementation before I go and make one myself tonight? Worth an ask :)

@fgatti675
Copy link
Member

H @SKLn-Rad, as far as I know this has not been implemented yet!
It would be great if you can share it when you are done so we can add it to the core

@jbxbergdev
Copy link

jbxbergdev commented Dec 23, 2022

Any news on the status of this? Would love to see all Firestore data types supported. Could somebody please share a code snippet that would be required to add support for Firestore geopoints? Thanks!

@fgatti675 fgatti675 added the Sponsorable If you need this feature, you can contact us for sponsoring it. Or submit a PR! label Dec 25, 2022
@fgatti675
Copy link
Member

This feature is not planned at this moment.
We are happy to merge PRs or to do sponsored developments!

@jbxbergdev
Copy link

jbxbergdev commented Dec 26, 2022

Ok, thanks for your reply. Could you share how the workaround you suggested, using custom fields would work? Something really simple, like giving the user a text field where comma separated latitude,longitude can be entered would work. Thanks!

@fgatti675
Copy link
Member

Hi @jbxbergdev
You have an example on how to implement a custom field here:
https://firecms.co/docs/properties/custom_fields

@jbxbergdev
Copy link

jbxbergdev commented Dec 29, 2022

@fgatti675 hm ok. I tried a custom field implementation as per your link for the type GeoPoint. However, in the CMS, I still get the error "Currently the field geopoint is not supported". Any idea what I'm doing wrong?
Code:

export default function GeopointField({
                                                 property,
                                                 value,
                                                 setValue,
                                                 customProps,
                                                 touched,
                                                 error,
                                                 isSubmitting,
                                                 context,
                                                 ...props
                                             }: FieldProps<GeoPoint, any>) {


    return (
        <>
            <TextField required={property.validation?.required}
                       error={!!error}
                       disabled={isSubmitting}
                       label={property.name}
                       value={ value.latitude + "," + value.longitude }
                       onChange={(evt: any) => {
                        const latLon: string[] = evt.target.value.split(",");
                        const lat: number = parseFloat(latLon[0]);
                        const lon: number = parseFloat(latLon[1]);
                        const geopoint = new GeoPoint(lat, lon);
                        setValue(geopoint);
                       }}
                       helperText={error}
                       fullWidth
                       variant={"filled"}/>

            <FieldDescription property={property}/>
        </>

    );

}

// ....
const eventCollection = buildCollection<Event>({
  // (...)
  properties: {
   // (...)
    location: {
      name: "Ort",
      dataType: "geopoint",
      Field: GeopointField
    },
// (...)
});

@fgatti675
Copy link
Member

Hi @jbxbergdev did you also link the field in your corresponding property?
{ dataType: "geopoint", name: "...", Field: GeopointField}

@jbxbergdev
Copy link

@fgatti675 ah yeah, I did. I'll update the code snippet. So in the create/edit view the field is showing the error message.

@singhvedant111
Copy link

Here is a working implementation. I have made the following react jsx element.

interface Location {
  lat: number;
  lng: number;
}

interface MapProps {
  apiKey: string;
  location: Location;
  zoom: number;
  onLocationChange: (location: Location) => void;
  onZoomChange: (zoom: number) => void;
}

const GoogleMapLocationPicker = ({
  apiKey,
  location,
  zoom,
  onLocationChange,
  onZoomChange,
}: MapProps) => {
  const [map, setMap] = useState<google.maps.Map | null>(null);
  const [marker, setMarker] = useState<google.maps.Marker | null>(null);

  useEffect(() => {
    const initMap = () => {
      const mapOptions: google.maps.MapOptions = {
        center: location,
        zoom: zoom,
      };
      const map = new google.maps.Map(
        document.getElementById("map")!,
        mapOptions
      );
      setMap(map);

      const marker = new google.maps.Marker({
        position: location,
        map: map,
        draggable: true,
      });
      setMarker(marker);

      marker.addListener("dragend", () => {
        const position = marker.getPosition();
        const location = {
          lat: position!.lat(),
          lng: position!.lng(),
        };
        onLocationChange(location);
      });

      map.addListener("zoom_changed", () => {
        const zoom = map.getZoom();
        onZoomChange(zoom!);
      });
    };

    if (!window.google) {
      const script = document.createElement("script");
      script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;
      script.onload = initMap;
      document.body.appendChild(script);
    } else {
      initMap();
    }
  }, [apiKey, location, onLocationChange, onZoomChange, zoom]);

  return <div id="map" style={{ height: "500px" }} />;
};

export default GoogleMapLocationPicker;

This can be used in a custom field as follows:

const DEFAULT_GEO_POINT = {
  lat: 37.749692,
  lng:-122.415284
};
export default function GeopointField({
  property,
  value = new GeoPoint(DEFAULT_GEO_POINT.lat, DEFAULT_GEO_POINT.lng),
  setValue,
  customProps,
  touched,
  error,
  isSubmitting,
  context,
  ...props
}: FieldProps<GeoPoint, any>) {
  const [defaultposition, setDefaultPosition] = useState(DEFAULT_GEO_POINT);
  const [defaultzoom, setDefaultZoom] = useState(15);
  return (
    <>
      <p>{property.name}</p>
      <GoogleMapLocationPicker
        apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY}
        location={
          value["_lat"]
            ? { lat: value["_lat"], lng: value["_long"] }
            : defaultposition
        }
        zoom={defaultzoom}
        onZoomChange={(zoom) => {
          setDefaultZoom(zoom);
        }}
        onLocationChange={(location) => {
          setDefaultPosition({ lat: location.lat, lng: location.lng });
          setValue(new GeoPoint(location.lat, location.lng));
        }}
      />
      <FieldDescription property={property} />
    </>
  );
}

This can be added to your collection as follows:

export const interventionCollection = buildCollection<Intervention>({
 ...
  properties: {
    ...
    location: buildProperty({
      name: "User Location",
      Field: GeopointField,
      validation: { required: true },
      dataType: "geopoint",
      description: "Select the location",
    }),
  },
});

@fgatti675
Copy link
Member

Awesome! Think you could submit a PR?
You can start by checking how other fields are integrated in this file:
form_field_configs.tsx

@singhvedant111
Copy link

Sure, I'll try and do that sometime this week @fgatti675 .

@lukecarbis
Copy link

@singhvedant111 Did you ever get around to this?

@lukecarbis
Copy link

@singhvedant111 – I like your map implementation. I ended up working something out based on yours that's a simple text input, accepting a comma separated latitude, longitude.

import React, { FunctionComponent } from "react";
import { FieldDescription, FieldProps, GeoPoint } from "firecms";
import {TextField} from "@mui/material";

const GeoPointField: FunctionComponent<FieldProps<GeoPoint>> = ({
    property,
    value,
    setValue,
    error,
    isSubmitting,
}) => {
    const geoPointToString = (geoPoint: GeoPoint) : string => {
        return `${geoPoint.latitude}, ${geoPoint.longitude}`;
    }

    const stringToGeoPoint = (str: string) : GeoPoint => {
        const [latitude, longitude] = str.split(',');
        return new GeoPoint(parseFloat(latitude), parseFloat(longitude));
    }

    return (
        <>
            <TextField
                required={property.validation?.required}
                error={!!error}
                disabled={isSubmitting}
                label={property.name}
                helperText={error}
                fullWidth
                variant={"filled"}
                value={value?.latitude && value.longitude ? geoPointToString(value) : ''} onChange={(evt) => {
                setValue(
                    stringToGeoPoint(evt.target.value)
                );
            }}/>
            <FieldDescription property={property} />
        </>
    );
}

export default GeoPointField;

@BartlomiejLewandowski
Copy link

I'm having a bit of trouble implementing this custom field. The implementation complains that the field is not supported.
Currently I have fallen back to using the string dataType and mapping in the component.

Are custom fields not supported? How does one use a different datatype?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers Sponsorable If you need this feature, you can contact us for sponsoring it. Or submit a PR!
Projects
None yet
Development

No branches or pull requests

7 participants