AR-GeoHPC
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ## Atelier HPC SAGEO 2025 ### Session 24/02/2025 #### Besoin Cas d'application géomatique/raster Chaine de traitement vue sur 3 outils différents - OpenMOLE - Jupyter avec Python - Jupyter avec R - {targets} + clustermq + SLURM #### Hébergement - IN2P3 : pas possible - Relancer CRIIAN - Onyxia - https://datalab.sspcloud.fr/catalog/ide?search=para - DASK - [Argo workflows](https://argo-workflows.readthedocs.io/en/latest/) - #### Supports Supports: - Notebook Jupyter (Python/R) - Git action de recherche MAGIS (OpenMole) Humanum forge AR HPC : https://gitlab.huma-num.fr/ar-magis-hpc #### Presentation Chaine de traitement Classification supervisé image sentinel 2, peupleraies Image fourni par tuile (100 * 100km) => 90 tuiles Modèle Random Forest échantillon entrainement classif supervisé => peuplier qui nous intéresse A deployer sur les 90 tuiles, utilisation du Cluster CNES Carte classification peuplier non peuplier, avec carte de confiance pour chaque pixel. Pas parfait, parfois peuplier la ou il n'y en a pas. On avait pas de masque pour classer foret/nonforet donc il fallait faire du post traitement. Trouver les règles expertes pour ne pas avoir besoin de ce masque hors forêt. Classif france entière, 2017 - 2022, 90 tuiles par année, 90 cartes confiance pour ces 90tuiles => post traitement Code dans un notebook Jupyter - ? pour R / Rasterio python - [rgdal2](https://github.com/thk686/rgdal2) pour R / equivalent python - https://cran.r-project.org/web/packages/gdalraster/index.html - https://cran.r-project.org/web/packages/vapour/index.html - https://github.com/gearslaboratory/gdalUtils (obsolete ?) - [stars](https://r-spatial.github.io/stars/articles/stars1.html) ? [terra] (https://rspatial.github.io/terra/index.html) ? pour R / Scipy pour python ##### 1ere etape Binarisé les cartes de 1 à 8 (une par essence forestière), recup pixel avec peuplier Intéressant ou pas côté pédagogie ? ##### 2eme etape Conserver les valeurs de confiance pour les peuplier : lire l'image de classif et garder que la confiance associé au peuplier => valeur égale a 1 Traite par paire 1 carte classif, 1 carte confiance ##### 1 er post traitement : masque de pente Seuil de pente, peuplier pousse pas au dela de 30%, donc on masque ce qui est au dessus. MNT IGN => pente à l'échelle nationale Pour chaque tuile, garde pixel si pente < 30% Masque avec GDALCalque ##### 2 eme post traitement : masque de hauteur Parfois confiance elevé, donc regarder aussi en fonction d'un seuil de haute. Si <3m c'est pas de la forêt pour enlever les cultures Carte nationale hauteur arbre, echelle france Pour chaque tuile, correspondance avec <3m, et on filtre. ##### 3 eme post traitement : médian, lissage Lissage pixel ##### 4 eme post traitement : masque de pente et hauteur Si malgré tout çà on a une confiance < 50% c'est que c'est pas du peuplier On a testé 30 et 50% ##### 5 eme post traitement : tamisage groupe de pixel Tamisage pour > 0.5 hectares (50 pixels) **Perspective :** Comparer les classifications en multi-temporels, comparaison entre années pour chercher la cohérence entre année sur ce qui est ou pas peuplier. Coupe c'est facile, mais quand c'est de l'aléatoire d'une année sur l'autre c'est problématique. #### Scripts (durée, complexité) Pur Python, pas d'optimisation, pas de paralléisation sur lecture gdal/raster => parallélisable : - interne au niveau de la tuile - entre tuile - entre années 1 semaine de traitement pour france entière, Dell 4 coeurs, 8go ram - 1 tuile : 2 Go - 46Go Raster pour france entière pour 6 année ? (à vérifier) #### Progression pédagogique Matin : - Introduction HPC => intro charabia HPC, mesocentre, etc. - Introduction Chaine de Traitement : raster, etc. - OpenMOLE : - introduction openmole Verrous : Quid des données un peu grosses => stockage in situ mésocentre, ou stockage LAL ? - raster chaine traitement peuplier - intro vecteur si il y a le temps => vector matching Après midi : **Introduction au raster et aux opérations ** - lecture, écriture avec GDAL - manipulation simple de raster => application à la binarisation **HPC** Paralélisation entre noeuds, paralléisation sur plusieurs CPU d'un même noeud (gdal xarray numpy?) *TODO LIST* - Forge humanum : - créer dépot Atelier - créer site web associé - A test (exemple minimum), deux notebooks différents (SEB pour le 28) - quarto + jupyter python/R => un peu lourd (lance un serveur Jupyter) - jupyter python/R => myst - Relance mail + voir ensuite Jocelyn / Onyxia (Seb) - Multilingue Python/R => regarder les équivalents Python/R (Nicolas) - script OpenMOLE pour python : commencer (Juste) - Preparation données, sous jeu de données uploadé => (Yousra) => Sharedocs ou autre (en fonction de la réponse humanum) ? - Voir Dask et targets(R) => (tous/toustes en fonction des affinités et du temps dispo) - Mail DSI Avignon avec file les infos des ip publiques ExModelo(SEB) ### Session 28/02/2025 - Zone étude : Nouvelle Aquitaine 27 tuiles - tuiles de confiance : quelques Mo environ - tuiles de classification : 500 Mo environ - tuiles de pente MTN 10m: 1 Mo par tuile, France entière 22800 dalles (22,8 Go) - Carte de hauteur : 22 Go France entière - Problématique résolue pour le mésocentre calcul, çà sera le CRIANN à Rouen sur leur espace Jupyter , notre contact c'est Benoist Gaston : https://services.criann.fr/services/hpc/cluster-austral/guide/jupyter/ - Installation de R via conda/mamba - https://www.happykhan.com/posts/conda-install-r/ - Conversion base de code vers R - vapour est proche de GDAL mais ne fait que lire des fichiers donc petite déception - Il est possible de faire des appels vers GDAL avec system() et link2GI1 semble pourvoir aider à s'assurer que le bouzin fonctionne - terra est devenu l'outil de référence pour la manipulation de raster mais il faut retrouver les fonctions qui vont bien. De plus, avec [geotargets](https://geotargets.njtierney.com/), ça va bien dans le sens où on veut aller - stars est orienté cube de données mais propose des fonctions intéressantes (ici une comparaison avec le paquet raster qui a été déprécié) - [gdalraster](https://usdaforestservice.github.io/gdalraster/) binding directe à GDAL mais pas compatible avec geotargets encore (à creuser ? en profiter pour contribuer ?) - Les deux méthodes qui m'inquiètent le plus sont celles qui proviennent de scipy.ndimages : `median_filter` et `generic_filter`. Pas sûr de pouvoir trouver un équivalent R (je n'ai pas approfondi plus que ça). - `generic_filter` n'est pas appelé - Appels Numpy à traduire - `np.nan_to_num()` - application de la valeur médiane aux pixels qui ont une valeur nulle - Remplacement dans terra : https://rspatial.github.io/terra/reference/subst.html - https://rspatial.github.io/terra/reference/focal.html - `np.nanmedian()` - calcul de la valeur médian des pixels valides (sans les NA) - Test jupyter Lab / Jupyter Server avec Jupyterbook v2, çà marche pas trop mal, mais on n'échappe pas à l'execution d'un kernel jupyter dans tous les cas. - Toutefois c'est moins lourd que ce ne que je pensais en terme d'install, pas plus que R en tout cas. - Je vais tester ensuite on peut faire un notebook R aussi avec Myst / JupyterLab ### TODO : - Forge avec fichier myst Python/R => Seb - Fichier yaml conda (pour le 13 ): - Windows et Linux => yousra (nico est sous windows) - Environnement R => Nicolas, seb - Environnement Python /=> Yousra, seb - Penser la décomposition / optimisation - Soit une grosse fonction avec toutes les étapes - Fonction `smooth_all_rasters()` qui opère sur toutes les tuiles avec une boucle, paf on la met en fonction et on la daskifie/targetifie - Test sur Workstation du Notebook => temps / durée du calcul en multi coeur pour info à Benoist Gaston => Sébastien, Yousra - Soit plein de petite fonctions - Création tout de suite de l'arborescence de fichiers pour optimiser - Pédagogie : - Penser à l'explication du passage de code sous forme de boucle à un code executable HPC / Dask et Targets - Peut etre ajouter des exercices, en se basant sur la chaine existante : tamisage, etc. => créer une nouvelle targets, ou un nouveau truc dask ### Session 05/03/2025 - Yousra a déposé le jeu de données lundi sur Humanum, possibilité d'y accéder via webdav - Yousra a commencé à reprendre le code pour diminuer les dépendances (Rasterio -> GDAL direct) - regénération des données intermédiaires, blocage sur la pente - COG proposé comme format alternatif pour stocker les tiff, avec du streaming, - Voir [Optimiser vos rasters et générer des mosaïques au format COG (Cloud Optimized GeoTIFF) avec GDAL](https://geotribu.fr/articles/2025/2025-02-11_bonnes-pratiques-generation-raster-cog-avec-gdal/) - Utilisation de COG avec OpenMole, reste la question de comment on charge les données ? - Création des comptes CRIANN - dépôt de dossier pour débloquer un compte pour Juste - Demande de contact DSI en cours pour éviter les problèmes de connexion - Jupyter sur le CRIANN - 1 répertoire pour chaque langage - 1 document identique - à pousser sur la forge - tutoriel à rédiger à propos de Myst - Premiers essais avec targets/geotargets/terra (Nicolas) ### Session 13/03/2025 Dask : - Optimisation inter images - Optimisation intra image, découpage en chunk, mais pas forcément possible avec les images qui ont besoin de calculer des voisinnages ... Ressources : - Chaine => GDAL--> rasterio --> xarray / RioXArray --> Dask - Rasterio XArray: https://docs.xarray.dev/en/stable/user-guide/io.html#rasterio & https://corteva.github.io/rioxarray/stable/readme.html - Rasterio Rio XArray : https://corteva.github.io/rioxarray/stable/getting_started/getting_started.html - GeoTIFF : https://knowledge.dea.ga.gov.au/notebooks/How_to_guides/Opening_GeoTIFFs_NetCDFs/ - XArray et Dask : https://docs.xarray.dev/en/stable/user-guide/dask.html#parallel-computing-with-dask - Cog + Geotiff + Xarray : https://gallery.pangeo.io/repos/pangeo-data/landsat-8-tutorial-gallery/landsat8.html#Dask-Chunks-and-Cloud-Optimized-Geotiffs - Fenetre mobile avec function XArray : https://tutorial.xarray.dev/intermediate/01-high-level-computation-patterns.html#sliding-windows-of-fixed-length-rolling et https://tutorial.xarray.dev/intermediate/01-high-level-computation-patterns.html#apply-an-existing-numpy-only-function-with-reduce Visu : https://examples.holoviz.org/gallery/geospatial.html & https://tutorial.xarray.dev/intermediate/hvplot.html #### Discussions Benoist Gaston (Criann) - Jupyter Hub fonctionnel - des demandes pour des kernel R donc plutôt motivé pour mettre en place Pour le Dasbboard il y a un proxy à renseigner (clic sur bouton marche pas) - Compte eleves générique déjà existant, on va passer par çà, soit avec des comptes liés à un projet spécifique, mais en tout cas il faut que çà soit homogene. Soit projet associé à la formation, soit création d'un projet spécifique, mais il faudra la liste des eleves/apprenants => Volumétrie 20 ok, avec une première date de fin préinscription au 15 avril. Interface StartMyServer => choix d'un profil de serveur (défini par Benoist en fonction des besoins) Volumétrie à définir, réservation des temps de calcul peut se faire plus tardivement (pas la veille evidemment, 2 semaines au préalable ?) Espace de stockage : Quota par défaut : 50 Go par utilisateur, quota par projet si 100 Go : 2 années, sortie qq mégas par image AUSTRAL: principalement job Python mais communauté IA demande Jupyter pour calibrer les modèles Kernel R à positionner Gestion de l'environnement conda et pip Arborescence des fichiers : pas possible de le définir par avance mais possibilité d'enregister la dernière position Dask peut lancer SLURM Affichage du dashboard (Cluster Map et Task Stream) Worker : nombre à définir 4 min -> 24 max Jupyterlab dans des job slurm, avec var d'environnement affecté Donc configuration d'un job/slurmcluster qui serait pas compatible, et il faudrait mettre en commentaire. => pas tout à fait le meme environnement qu'en local. Point d'attention : serveur de 192 coeurs de calcul => plusieurs utilisateurs sur une machine, donc plusieurs ports ouverts par DASK pour afficher le dashboard Session Jupyter exécutée sur un serveur, dans le cadre de la session, Job SLURM pas associé au job Jupyter mais variables d'environnement SLURM pour Jupyter peuvent être transmises. Montrer passage à l'échelle entre LocalCluster (noeuds dédiés Jupyter, ressources moindres) et avec SLURMCluster. On peut associer fichier de profil pour le job qui sera lancé par SLURM dans le jupyter. On peut associer une resa, certain nombre d'item déjà pris en compte. Mieux de faire une résa CPU, il faudra le prévoir un peu en amont de notre coté. C'est mieux que cela soit explicite plutot que d'avoir un profil préconfiguré. Sur les noeud Austral on a REDHAT. Installation à la main sur de la RED HAT, dans des rep spécifiques. Outil module pour charger et décharger les variables d'environnements. Conda pour installer, mais module pour charger ces environnements là. Pour les YML : export ### Session 20/03/2025 #### image Docker - choix debian Bookworm/unstable: - bookworm : Python 3.10 (ok)mais R 4.2 (en retard, pas tous les packages dispo) - unstable : Python 3.13 (trop avancé) mais R 4.4 - décision partir de bookworm puis installer R 4.4 ### Code R `terra::focal()` devrait pouvoir remplacer le filtre median. - Taille de la matrice 3x3 pixels - Plusieurs options sont disponibles et doivent être évaluées: - option par défaut `na.rm=FALSE` `focal(r, 3, f)` - option `na.rm=TRUE` `focal(r, 3, f, na.rm=TRUE)` - option `na.policy="only"` : change uniquement les cellules qui sont des NA `focal(r, 3, f, na.policy="only", na.rm=TRUE)` - option `na.policy="omit"` : ne change pas les cellules NA `focal(r, 3, f, na.policy="omit", na.rm=TRUE)` Les options `na.rm=FALSE` et `na.policy="omit"` peuvent être écartées car le code python remplace les NA par une valeur médiane (à l'échelle de l'image ? mais pas de la fenêtre). Si `na.rm=TRUE`, alors le pixel considéré prend la valeur de la médiane des pixels adjacents hors NA. Mais pour `na.policy="only"`, qu'est-ce qu'il se passe ? Rappel que le but de l'atelier est de mettre en oeuvre la parallélisation si les résultats différentent en python et R ce n'est pas très important. La comparaison peut être utile si on va jusqu'à une publication. #### Code de python - problème compatibilité gdal/rasterio/xarray - Yousra a trouvé une solution - on créé deux md pour documenter les deux types de distributions #### Distribution - 1 année , distribution sur 27 tuiles - point où appeler la parralélisation à identifier - remplacer boucles for qui parcours les tuiles - chaque tuile est traitée de manière indépendante, pas de gestion de la frontière - Parraléliser dans le `main()` mais pas dans les fonctions - Pour une tuile faire toute la chaine - ou pour chaque fonction, passer toutes les tuiles - temps de mise en route, - 27 jobs vs (27 tuiles x 6 étapes) jobs - gestion des entrées/sorties pour chaque étape - si 1 job, les étapes intermédiaires peuvent être conservées en mémoire mais pas écrites dans un ficier - dépends de comment sont configuré SLURM - 30 s en amont et en sortie du job ### Session 26/03/2025 - Utiliser GDAL sans utiliser xarray - essai Yousra: fonctionne pour les 3 premières étapes - fonctionne sur les threads mais pas les process - problème avec GDAL ? liaison GDAL/Dask pas documenté - objet GDAL pas sériables => blocage au niveau du format - Conversion rasterio => xarray faisable - plutôt simple - plus pythonique qu'avec GDAL - env python (librairies geospatiales) - proj=9.6.0=h0054346_0 - libgdal-core=3.10.2=hae73b24_2 - gdal=3.10.2=py310h79127d3_2 - geos=3.13.1=h97f6797_0 - geotiff=1.7.4=h239500f_2 https://rasterio.readthedocs.io/en/stable/topics/switch.html https://pangeo-data.github.io/pangeo-openeo-BiDS-2023/part3/data_exploitability_pangeo.html ### Session 30/04/2025 - Mail aux participants (avec OpenSSH ? A voir avec la réponse), venir avec un Navigateur + Internet *Todo =>* Sébastien - Verifier présence d'un poste de démo dans la salle sinon voir avec la DSI pour au moins un accès filaire + numéro téléphone DSI / Benoist "aucasou"" - Problème de seuil sur la fonction Python, mais Yousra va corriger - Comparaison entre le python et le R en terme de résultats - Définition du `pattern` dans `tar_target` avec un map - [Exemple iceBreaker](https://github.com/njtierney/icebreaker/blob/5e330d6bc941e006880e2430d9c7d24016a21a76/_targets.R#L38C1-L42C5) - Définition du pattern dans [targets](https://github.com/njtierney/icebreaker/blob/5e330d6bc941e006880e2430d9c7d24016a21a76/_targets.R#L38C1-L42C5) - Programme Matin : - Chapitre 0 : Présentation atelier (10 - 15 min) - Motivations - Objectifs - Remerciements - Chapitre 1 : Présentation de la chaine de traitement (45 min) - Les données (et les formats) () - Pourquoi pas gdal directement ? - La chaine de traitement (30 min) - Diagramme d'exécution des traitements (extrait du rapport) - Structure de données utilisés, equivalence (Python / R) - Explication chaine blocs (10 min) - Chaine de bloc initialement codé : la boucle - Type de Parallélisation envisagés - Niveau de l'année - Niveau de la tuile => (tous les traitements sur 1 tuile) * nb tuiles - Niveau de la tuile => (1 traitement pour l'ensemble des tuiles) * nb traitements - Chapitre 2 : Chaine de traitement - Présentation librairies utilisés - Présentation Format Données (Xarray, SpatDataRaster, etc.) - Execution de la chaine initiale (loop) sur une 1 tuile - Calcul pour voir que c'est impossible - Mise en pratique de la parallelisation de cette chaine - Chapitre 2.1 Fonction 1 - Bloc Python / Bloc R / Comparaison - Chapitre 2.3 Fonction 2 - Chapitre 3 : Parallélisation (début après midi) - 3.1 avec R - présentation targets => [reprendre élément doc Nicolas](https://gt-notebook.gitpages.huma-num.fr/workshop/je_notebook_2024/notebooks-r-plus-reproductibles/support/) (programmation fonctionnelle, Make-like) - présentation géotargets => à faire - présentation cluster / crew - présentation SLURM vs LOCAL - 3.2 avec Python - présentation Dask - présentation Xarray avec Dask => A faire - présentation Future vs Map => Différentes façon de faire - présentation SLURM vs LOCAL - Exemples simple avec spatial - Chapitre 4 : Perspectives / Conclusion - Cache ? Rapidité ? - support de cours - quarto : possibilité de générer le doc sans kernel - Myst : nécessite un kernel pour builder le document - Dans un dépot séparé pour le moment - => https://gitlab.huma-num.fr/ar-magis-hpc/workshops/atelier-hpc-notebooks/website-hpc-sageo-2025# - Todo => CI Seb/Nicolas

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully